Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

One DbContext per request in ASP.NET MVC (without IOC container)

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.

like image 234
devuxer Avatar asked Jun 13 '11 18:06

devuxer


People also ask

Is DbContext thread safe?

DbContext is not thread-safe. Do not share contexts between threads. Make sure to await all async calls before continuing to use the context instance. An InvalidOperationException thrown by EF Core code can put the context into an unrecoverable state.

Should DbContext be scoped or transient?

But if you use other injected services, with "transient" on the DBContext, every service gets his own instance of it. In order to avoid that you should always use "scoped" on the DBContext. Correct. Maybe there are cases in which you need a transient EF-Context - but usually you should stick to scoped.


2 Answers

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.

like image 112
walther Avatar answered Sep 20 '22 03:09

walther


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; }     } } 
like image 30
Chad Moran Avatar answered Sep 20 '22 03:09

Chad Moran