One DbContext per request in ASP.NET MVC (without

2019-01-01 12:18发布

问题:

Apologies if this has already been answered, but how do you guarantee one Entity Framework DbContext per request if you are not using an IOC container? (The answers I\'ve come across so far deal with IOC container solutions.)

It seems like most solutions hook into the HttpContext.Current.Items dictionary, but how do you guarantee disposal of the DbContext when the request is finished? (Or is disposal not absolutely necessary with an EF DbContext?)

Edit

I\'m currently instantiating and disposing my DbContext in my controllers, but I also have several separate instantiations of my DbContext in ActionFilters and my MembershipProvider (and I just noticed, also a couple validators). So, I thought it might be a good idea to centralize instantiation and storage of my DbContext to reduce overhead.

回答1:

I would use the BeginRequest/EndRequest method, this helps ensure that your context is disposed of properly when the request is over with.

protected virtual void Application_BeginRequest()
{
    HttpContext.Current.Items[\"_EntityContext\"] = new EntityContext();
}

protected virtual void Application_EndRequest()
{
    var entityContext = HttpContext.Current.Items[\"_EntityContext\"] as EntityContext;
    if (entityContext != null)
        entityContext.Dispose();
}

And in your EntityContext class...

public class EntityContext
{
    public static EntityContext Current
    {
        get { return HttpContext.Current.Items[\"_EntityContext\"] as EntityContext; }
    }
}


回答2:

I know this is not a recent question, but I\'ll post my answer anyway, because I believe someone may find it useful.

As probably many others, I followed the steps mentioned in the accepted answer. Yay, it works. HOWEVER, there\'s one catch:

Methods BeginRequest() and EndRequest() fire each time a request is made, but not only for aspx pages, but for ALL STATIC CONTENT! That said, if you use the code mentioned above and you have on your page let\'s say 30 images, you\'re re-instantiating your dbcontext 30 times!

The solution for this is to use a wrapping class for retrieving the context, something like this:

internal static class ContextPerRequest
{
      internal static DB1Entities Current
      {
          get
          {
              if (!HttpContext.Current.Items.Contains(\"myContext\"))
              {
                  HttpContext.Current.Items.Add(\"myContext\", new DB1Entities());
              }
              return HttpContext.Current.Items[\"myContext\"] as DB1Entities;
          }
      }
 }

And then for disposing

protected void Application_EndRequest(object sender, EventArgs e)
{
   var entityContext = HttpContext.Current.Items[\"myContext\"] as DB1Entities;
   if (entityContext != null) 
      entityContext.Dispose();
}

This modification ensures that you instantiate and dispose your context only once per request and only when needed. Selected answer instantiates context every single time.

Note: DB1Entities is derived from DbContext (generated by VS). You would probably want to alter it with your context name ;)

Note 2: in this example I\'m working with just one dbcontext. If you need to work with multiple, you would need to modify this code according to your needs. Don\'t take this as some ultimate solution to world problems, because it certainly isn\'t a final product. It is meant just to give a hint, how it may be achieved in a very easy way.

Note 3: Same approach can be used in different situations as well, for instance when you\'d like to share an instance of SqlConnection or any other... This solution isn\'t exclusive to DbContext object, nor to Entity framework.



回答3:

One way would be to subscribe for the Application_BeginRequest event, inject the DbContext into the current HttpContext and in the Application_EndRequest fetch from the HttpContext and dispose. Anything in between (which is pretty much everything :-)) could fetch the DbContext from the current HttpContext and use it. And, yes, you should dispose it. And by the way is there any reason you don\'t use a DI framework which already does this for you among other useful things?



回答4:

Small addition for Chad Moran answer. It is inspired by walther notes. To avoid context initialization for static content we should check current route handler (this example only for MVC):

protected virtual void Application_BeginRequest()
{
  var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(this.Context));
  if (routeData != null && routeData.RouteHandler is MvcRouteHandler)
  {
    HttpContext.Current.Items[\"_EntityContext\"] = new EntityContext();
  }
}


回答5:

If you implement IDisposable in your controller, and dispose context in disposing method, and instantiate new context in controller constructor, you should be safe as controller is instantiated for each request. I don\'t see, however, why would you want to do that? ... You should use DI, or make a context factory with one static instance of context. If you don\'t use one instance (you make one for each request) you are to have problems at some point. The problem with undisposed context is that EF caches data in context, and if some other instance of context changes something in DB that is already cached in another context - you have unconsistent state. Before DI became so popular, I used to have one static instance of context somewhere in application, and that is a lot faster and safer than having each request make its own context, but you need to implement state-checking code which makes sure that context connection to db is ok... There are a lot better solutions to this problem, and the best is to use some DI framework. I would recommend Ninject in combination with MVCTurbine, it is easy to set up and you can add it through NuGet.



回答6:

The slippery slope here is having inconsistent state. If you\'re app is going to have multiple users on it, and they have the potential to change data concurrently then you might start running into problems with data integrity if you keep a single context.