how to decouple controller from Http context session - asp.net-mvc

I need to store some data in session inside my action however I'm concerned about coupling my controller to the http context session, I have thought about creating a service, but is it really worth it?

No, it isn't worth it. It is the controller that has access to the Http Context including the session. Not to mention that you already are working with an abstraction of the session: HttpSessionStateBase which can be easily mocked in a unit test.
There might be situations where you could have your business methods take ICollection as input parameter which is an interface implemented by HttpSessionStateBase and then have the controller pass the Session object to them.

Especially for ApiControllers, build yourself a DelegatingHandler and push all of your goodies onto request.Properties. You can then retrieve them from your request whether you are testing or running live. The benefit is that you then have zero dependency on Session in your Controller.
MessageHandler
public class ContextHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
// get the goodies to add onto the request
var goodies = /* call to goodieGoodieYumYum */
// add our goodies onto the request
request.Properties.Add(Constants.RequestKey_Goodies, goodies);
// pass along to the next handler
return base.SendAsync(request, cancellationToken);
}
}
Controller Action
var goodies = (List<Goodie>)Request.Properties[Constants.RequestKey_Goodies];

Related

How to delay transaction open in EF6?

We use ASP.Net MVC + Autofac + EF6.
DbContext is wrapped by UnitOfWork. We create it for each http request via Autofac.
We also open transaction for whole http request in UnitOfWork constructor.
The problem is that not all http requests have to be wrapped in to the transaction. Some of them even don't have requests to DB.
We'd like to delay transaction open till the first actual request to DB.
Any ideas how one can do it?
We can override SaveChages and open transaction before save, but select queries will not be executed in transaction this way.
One more problem here: we use global filters from EF Plus for soft removable entities. It works good, but filters initialization for context is rather slow. We'd like to delay it till the first actual request to DB too.
The problem is that your UnitOfWork is injected into controller despite an action being called and therefore its constructor is always called even if you don't need to use it. One solution could be using Autofac lazy injection. In this case UnitOfWork's constuctor is called only when its instance is needed
public class SomeController : Controller {
//..
private readonly Lazy<IUnitOfWork> _unitOfWork;
private IAnotherService UnitOfWork => _unitOfWork.Value;
public SomeController(
//..
Lazy<IUnitOfWork> unitOfWork
)
{
//..
_unitOfWork = unitOfWork;
}
//..
public ActionResult ActionNeedsTransaction()
{
//use UnitOfWork
UnitOfWork.SaveChanges();
return Json(value);
}
}

How to inject different concrete implementation into controller depending on current request action method attributes?

I currently have a custom PrototypingControllerFactory that looks for a custom [Prototype] attribute on the action method being invoked for the current request, and depending on whether the attribute is present or not will inject a different implementation of an interface ISomeService. (In this case ISomeService more or less abstracts a messaging service, so the mock implementation allows returning "canned" results when the real implementation is not yet ready to handle a particular message).
So for example, if I have a controller class like so:
public class MyController : Controller
{
private readonly ISomeService _someService;
public MyController(ISomeService someService /*, .... other dependencies */
{ _someService = someService; //... etc }
public ActionResult Action1()
{
//...
_someService.SomeMethod();
//...
}
[Prototype]
public ActionResult Action2()
{
//...
_someService.SomeMethod();
//...
}
}
Then when Action1 is invoked from an http request, _someService should use the production implementation of ISomeService, but when Action2 is invoked, _someService should have a Mocked version of ISomeService.
From a strict design standpoint, I realize this might point to having too many actions in a particular controller (otherwise, for example, I could just mark an entire controller as having [Prototype]) , but due to project inertia, I would rather not try to force a change in how actions are placed in controllers.
Currently the autofac registration has the following:
builder.RegisterControllers(Assembly.GetExecutingAssembly());
if (ConfigurationManager.AppSettings["AllowPrototyping"] == "true")
{
builder.RegisterType<PrototypingControllerFactory>().As<IControllerFactory>().InstancePerRequest();
}
However, this means that the controller factory has to do some fancy work to figure out constructor arguments, and get instances from the DI container. Recently, I have discovered some subtle differences in behavior between the custom controller factory, and the "real" controller factory that are not desirable.
I would like to eliminate the custom controller factory, and instead have autofac fully handle resolution of the controllers.
How can I tell autofac to resolve a different implementation of an interface depending on whether the currently executing action is decorated with my custom [Prototype] attribute?
The first option depends on reliably being able to determine the controller action method directly from the route data, which is not necessarily straightforward. Part of the issue is that creation of the controller (and hence injection of the dependencies) occurs fairly early in the process, even before authorization filters run.
If some reliable implementation of a magical method say ActionDescriptor GetActionDescriptor(RoutData routeData) actually existed, then I could do something like the following:
builder.Register<ISomeService>(c =>
{
var httpRequest = c.Resolve<HttpRequestBase>();
var actionDescriptor = GetActionDescriptor(httpRequest.RequestContext.RouteData);
if (actionDescriptor.GetAttributes<PrototypeAttribute>().Any())
{
return new PrototypeSomeService();
}
return new RealSomeService();
}).InstancePerRequest();
However the closest I could come to getting an ActionDescriptor at the point where the controller is intantiated was overriding DefaultControllerFactory, which is precisely what I am trying to get away from.
From default controller factory you can use the protected DefaultControllerFactory.GetControllerType() from which you could then create a ReflectedControllerDescriptor. But then you still have to do some munging to get the correct action descriptor from controllerDescriptor.GetCanonicalActions(). (In fact, I suspect this "munging" is leading to the subtle differences in the original custom controller factory). This could get even more complicated when routes are used from other http handlers (think Elmah or MiniProfiler for example).
In the end I opted to avoid attempting to map RouteData to an ActionDescriptor by making [PrototypeAttribute] inherit from ActionFilterAttribute so that I could easily hook into OnActionExecuting, from which point I added an identifier to the current HttpContext, e.g.
public class PrototypeAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.RequestContext.HttpContext.Items.Add("Prototype", "Prototype");
base.OnActionExecuting(filterContext);
}
}
Then I modified my PrototypeSomeService so that it wrapped an implementation of ISomeService and delegated to it if the context did not contain the prototype key, e.g.
public class PrototypeSomeService : ISomeService
{
private readonly ISomeService _wrappedService;
private readonly HttpRequestBase _httpRequest;
public PrototypeSomeService(ISomeService wrappedService, HttpRequestBase httpRequest)
{
_wrappedService = wrappedService;
_httpRequest = httpRequest
}
public object SomeMethod()
{
if(_httpRequest.RequestContext.HttpContext.Items.Contains("Prototype"))
return _wrappedService.SomeMethod();
//other prototype logic...
return prototypeResult;
}
}
The final piece to tie it all together is to use autofac's decorator capabilities:
var someServiceRegistration = builder.RegisterType<SomeService>().InstancePerRequest();
if (ConfigurationManager.AppSettings["AllowPrototyping"] == "true")
{
someServiceRegistration.Named<ISomeService>("Prototype");
builder.RegisterDecorator<ISomeService>(
(c, inner) => new PrototypeSomeService(inner, c.Resolve<HttpRequestBase>()),
fromKey: "Prototype"
);
}
else
{
someServiceRegistration.As<ISomeService>();
}
One small downside is that you have to make sure you use a unique key for the httpcontext item or else strange things will happen, but that is pretty easily avoided by e.g. using a guid.
This approach allowed me to leave most of the existing code unchanged, only modifying the prototype implementation of the service and the autofac registrations.
You can read more about autofac decorators at:
http://nblumhardt.com/2011/01/decorator-support-in-autofac-2-4/
http://docs.autofac.org/en/latest/advanced/adapters-decorators.html?highlight=decorator#decorators

Entity Framework Core 1.0 unit of work with Asp.Net Core middleware or Mvc filter

I am using EF Core 1.0 (previously known ad EF7) and ASP.NET Core 1.0 (previously known as ASP.NET 5) for a RESTful API.
I'd like to have some unit of work scoped to an http request in such a way that when responding to the HTTP request either ALL the changes made to the DbContext will be saved onto the database, or none will be saved (if there was some exception, for example).
In the past I have used WebAPI2 for this purpose with NHibernate by using an Action filter where I begin the transaction on action executing, and on action executed I end the transaction and close the session. This was the way recommended at http://isbn.directory/book/9781484201107
However now I am using Asp.Net Core (with Asp.Net Core Mvc although this should not be relevant) and Entity Framework which, I understood, already implements a unit of work.
I think having a middleware plugged into the ASP.NET pipeline (before MVC) would be the right way to do things. So a request would go:
PIPELINE ASP.NET: MyUnitOfWorkMiddleware ==> MVC Controller ==> Repository ==> MVC Controller ==> MyUnitOfWorkMiddleware
I was thinking of having this middleware save the DbContext changes if no exception happened, so that in my repository implementations I don't even need to do dbcontext.SaveChanges() and everything would be like a centralized transaction. In pseudocode I guess it would be something like:
class MyUnitOfWorkMiddleware
{
//..
1-get an instance of DbContext for this request.
try {
2-await the next item in the pipeline.
3-dbContext.SaveChanges();
}
catch (Exception e) {
2.1-rollback changes (simply by ignoring context)
2.2-return an http error response
}
}
Does this make sense? Does anybody have any example of something similar? I can't find any good practice or recommendation around this.
Also, if I go with this approach at my MVC controller level I would not have access to any resource ID created by the database when POSTing a new resource because the ID would not be generated until the dbContext changes are saved (later on in the pipeline in my middleware AFTER the controller has finished executing). What if I needed to access the newly created ID of a resource in my controller?
Any advice would be greatly appreciated!
UPDATE 1: I found a problem with my approach to use middleware to achieve this because the DbContext instance in the middleware is not the same as during the MVC (and repositories) lifetime. See the question Entity Framework Core 1.0 DbContext not scoped to http request
UPDATE 2:I haven't yet found a good solution. Basically these are my options so far:
Save the changes in DB as soon as possible. That means saving it on the repository implementation itself. The problem with this approach is that for an Http request maybe I want to use several repositories (i.e: save something in database and then upload a blob to a cloud storage) and in order to have a Unit of Work I would have to implement a repository that deals with more than one entity or even more than one persistance method (DB and Blob Storage), which defeats the whole purpose
Implement an Action Filter where I wrap the whole action execution in a DB transaction. At the end of the controller's action execution, if there are no exceptions I commit chanches to DB but if there are exceptions I rollback and discard the context. The problem with this is that my controller's action may need a generated Entity's Id in order to return it to the http client (i.e: If I get a POST /api/cars I would like to return a 201 Accepted with a location header that identifies the new resource created at /api/cars/123 and the Id 123 would not be available yet since the entity has not been saved in DB and the Id is still a temporary 0). Example in controller's action for a POST verb request:
return CreatedAtRoute("GetCarById", new { carId= carSummaryCreated.Id }, carSummaryCreated); //carSummaryCreated.Id would be 0 until the changes are saved in DB
How could I have the whole controller's action wrapped in a DB transaction and at the same time have available any Id generated by the database in order to return it in the Http Response from the controller? Or.. is there any elegant way to overwrite the http response and set the Id at the action filter level once the DB changes have been commited?
UPDATE 3: As per nathanaldensr's comment I could get the best of both worlds (wrapping my controller's action execution in a DB transaction _ UoW and also knowing the Id of the new resource created even before the DB commits changes) by using code generated Guids instead relying on database to generate the Guid.
As per Entity Framework Core 1.0 DbContext not scoped to http request
I could not use a middleware to achieve this because the instance of DbContext that the middleware gets injected is not the same as the DbContext during MVC execution (in my controllers, or repositories).
I had to go with a similar approach to save the changes in DbContext after the controller's action execution using a Global Filter.
There is no official documentation yet about filters in MVC 6 so if anybody is interested on this solution see below the filter and the way I make this filter global so that it executes before any controller's action.
public class UnitOfWorkFilter : ActionFilterAttribute
{
private readonly MyDbContext _dbContext;
private readonly ILogger _logger;
public UnitOfWorkFilter(MyDbContext dbContext, ILoggerFactory loggerFactory)
{
_dbContext = dbContext;
_logger = loggerFactory.CreateLogger<UnitOfWorkFilter>();
}
public override async Task OnActionExecutionAsync(ActionExecutingContext executingContext, ActionExecutionDelegate next)
{
var executedContext = await next.Invoke(); //to wait until the controller's action finalizes in case there was an error
if (executedContext.Exception == null)
{
_logger.LogInformation("Saving changes for unit of work");
await _dbContext.SaveChangesAsync();
}
else
{
_logger.LogInformation("Avoid to save changes for unit of work due an exception");
}
}
}
and the filter gets plugged into my MVC at Startup.cs when configuring MVC.
public void ConfigureServices(IServiceCollection services)
{
//..
//Entity Framework 7
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<SpeediCargoDbContext>(options => {
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]);
});
//MVC 6
services.AddMvc(setup =>
{
setup.Filters.AddService(typeof(UnitOfWorkFilter));
});
//..
}
This still leaves a question (see UPDATE 2 on my question). What if I want my controller to respond to an http POST request with a 201 Accepted with a Location header that includes the Id of the entity created in DB? When the controller's action finalises execution the changes have not yet been committed to DB therefore the Id of the entity created is still 0 until the action filter saves changes and the DB generates a value.
I am also facing the same issue and not sure which approach to follow.
One of the approach that I used is as follow:
public class UnitOfWorkFilter : ActionFilterAttribute
{
private readonly AppDbContext _dbContext;
public UnitOfWorkFilter(AppDbContext dbContext,)
{
_dbContext = dbContext;
}
public override void OnActionExecuted(ActionExecutedContext context)
{
if (!context.HttpContext.Request.Method.Equals("Post", StringComparison.OrdinalIgnoreCase))
return;
if (context.Exception == null && context.ModelState.IsValid)
{
_dbContext.Database.CommitTransaction();
}
else
{
_dbContext.Database.RollbackTransaction();
}
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.HttpContext.Request.Method.Equals("Post", StringComparison.OrdinalIgnoreCase))
return;
_dbContext.Database.BeginTransaction();
}
}
My advice, use dbContext.SaveChanges() in the controller as it is demonstrated in all examples over the web. What you want to do sounds quite fancy and could backfire as you guessed at the end of your post. And IMO, it doesn't make sense.
Regarding your second question/task:
....when responding to the HTTP request either ALL the changes made to the DbContext will be saved onto the database, or none will be saved (if there was some exception, for example).
I think you need something like 'transaction-per-request'. It is just an idea, haven't tested it at all. I just put the code together in this sample middleware:
public class TransactionPerRequestMiddleware
{
private readonly RequestDelegate next_;
public TransactionPerRequestMiddleware(RequestDelegate next)
{
next_ = next;
}
public async Task Invoke(HttpContext context, DbContext dbContext)
{
var transaction = dbContext.Database.BeginTransaction(
System.Data.IsolationLevel.ReadCommitted);
await next_.Invoke(context);
if (context.Response.StatusCode == 200)
{
transaction.Commit();
}
else
{
transaction.Rollback();
}
}
}
Good luck

async action filter in MVC for async controller methods?

Let me prefix first that there are already two questions from Stack Overflow with accepted answers that async action filters are not supported:
Async action filter in MVC 4
Calling Async Methods in Action Filters in MVC 5
So, I'm looking for a 'work-around'. This is what I currently do in my WebApi application:
public class MessageLoggingHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
await LogRequestInfo(request);
var response = await base.SendAsync(request, cancellationToken);
await LogResponseInfo(response);
return response;
}
What I am trying to achieve is to log the request and log the response as a cross cutting concern. How can I do this in MVC? Is it even possible? I see two posts with a possible solution... has anyone used it? Does it work? Should I even go down this path? Are there alternatives? I'm really hesitant to adopt either approach for fear of breaking something in our application.
1) Implement a custom AsyncControllerActionInvoker
http://ayende.com/blog/163170/building-async-unit-of-work-with-mvc-4
2) Create and Async Controller Base Class, uses the "ParallelExtensionsExtras" library (whatever that is), and overrides some of the BeginExecute, EndExecute methods.
Perform Async operation asp.net mvc outside of the action

ASP.NET MVC: ensure user always has a session variable set

Consider an ASP.NET MVC application that requires a session variable be set. It's used throughout the app. It'll be set by either reading a hashed value on the browser cookie, or after having the user login.
In the WebForms + Master Page model, I'd check the Page_Load() of the master page. Perhaps not the ultimate event, but it was an easy one to find.
How would you check and enforce the existence of a session variable in ASP.NET MVC? Consider that this question might not involve user login details, but some other piece of data (first visit time, perhaps).
Solution Attempts
public void Application_BeginRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Session["SomeDateTime"] = DateTime.Now.ToString();
// results in Object reference not set to an instance of an object.
// context.Session is null
}
You have two options.
1.Place logic in base controller's Initialize function
Assuming that all your controllers inherit from a base controller, you can place the logic needed in the override of the Execute() function of the base controller.
public class BaseController : Controller
{
public BaseController()
{
}
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
// check if the user has the value here using the requestContext.HttpContext object
}
{
2. Use the Global.asax void Application_PreRequestHandlerExecute(Object source, EventArgs e) function
public void Application_PreRequestHandlerExecute(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
// use an if statement to make sure the request is not for a static file (js/css/html etc.)
if(context != null && context.Session != null)
{
// use context to work the session
}
}
Note: The second part works with any ASP.NET application, WebForms or MVC.
As for enforcing that they have a certain session variable, its very open really. You can redirect to a certain page for them to fill out a form or select an option or something. Or maybe just have a default value that is set to a certain session key if it is not found.
EDIT
While playing with this, I noticed a big issue with Application_PreRequestHandlerExecute approach. The event handler is being called for any request done to the server, be it .css/.js/.html files. I'm not sure if this is an issue with the way my workstation is setup, or just how ASP.NET/IIS works, so I would make sure that this isn't being called on all requests when implementing the approach above.
It is for the previous reasons I wrapped the work to be done in the session with an if statement.
Not sure I fully understand the question, but I do this by override the OnActionExecuting method of the controller.
In there you do the code to see if the Session Variable exists. If not, create it, if so then use it.
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Session != null)
{
//TODO: Get value from session etc.
}
base.OnActionExecuting(filterContext);
}
As another alternative, the ControllerActionInvoker class invokes every action method; it gets assigned to the controller via the controller factory. So you could subclass this action invoker, everytime an action is invoked (by overridding the InvokeAction method) check here for this existence...

Resources