One DbContext per web request… why?

后端 未结 9 1194
死守一世寂寞
死守一世寂寞 2020-11-21 22:09

I have been reading a lot of articles explaining how to set up Entity Framework\'s DbContext so that only one is created and used per HTTP web request using var

相关标签:
9条回答
  • 2020-11-21 22:41

    What I like about it is that it aligns the unit-of-work (as the user sees it - i.e. a page submit) with the unit-of-work in the ORM sense.

    Therefore, you can make the entire page submission transactional, which you could not do if you were exposing CRUD methods with each creating a new context.

    0 讨论(0)
  • 2020-11-21 22:46

    Not a single answer here actually answers the question. The OP did not ask about a singleton/per-application DbContext design, he asked about a per-(web)request design and what potential benefits could exist.

    I'll reference http://mehdi.me/ambient-dbcontext-in-ef6/ as Mehdi is a fantastic resource:

    Possible performance gains.

    Each DbContext instance maintains a first-level cache of all the entities its loads from the database. Whenever you query an entity by its primary key, the DbContext will first attempt to retrieve it from its first-level cache before defaulting to querying it from the database. Depending on your data query pattern, re-using the same DbContext across multiple sequential business transactions may result in a fewer database queries being made thanks to the DbContext first-level cache.

    It enables lazy-loading.

    If your services return persistent entities (as opposed to returning view models or other sorts of DTOs) and you'd like to take advantage of lazy-loading on those entities, the lifetime of the DbContext instance from which those entities were retrieved must extend beyond the scope of the business transaction. If the service method disposed the DbContext instance it used before returning, any attempt to lazy-load properties on the returned entities would fail (whether or not using lazy-loading is a good idea is a different debate altogether which we won't get into here). In our web application example, lazy-loading would typically be used in controller action methods on entities returned by a separate service layer. In that case, the DbContext instance that was used by the service method to load these entities would need to remain alive for the duration of the web request (or at the very least until the action method has completed).

    Keep in mind there are cons as well. That link contains many other resources to read on the subject.

    Just posting this in case someone else stumbles upon this question and doesn't get absorbed in answers that don't actually address the question.

    0 讨论(0)
  • 2020-11-21 22:46

    Another issue to watch out for with Entity Framework specifically is when using a combination of creating new entities, lazy loading, and then using those new entities (from the same context). If you don't use IDbSet.Create (vs just new), Lazy loading on that entity doesn't work when its retrieved out of the context it was created in. Example:

     public class Foo {
         public string Id {get; set; }
         public string BarId {get; set; }
         // lazy loaded relationship to bar
         public virtual Bar Bar { get; set;}
     }
     var foo = new Foo {
         Id = "foo id"
         BarId = "some existing bar id"
     };
     dbContext.Set<Foo>().Add(foo);
     dbContext.SaveChanges();
    
     // some other code, using the same context
     var foo = dbContext.Set<Foo>().Find("foo id");
     var barProp = foo.Bar.SomeBarProp; // fails with null reference even though we have BarId set.
    
    0 讨论(0)
  • 2020-11-21 22:50

    There are two contradicting recommendations by microsoft and many people use DbContexts in a completely divergent manner.

    1. One recommendation is to "Dispose DbContexts as soon as posible" because having a DbContext Alive occupies valuable resources like db connections etc....
    2. The other states that One DbContext per request is highly reccomended

    Those contradict to each other because if your Request is doing a lot of unrelated to the Db stuff , then your DbContext is kept for no reason. Thus it is waste to keep your DbContext alive while your request is just waiting for random stuff to get done...

    So many people who follow rule 1 have their DbContexts inside their "Repository pattern" and create a new Instance per Database Query so X*DbContext per Request

    They just get their data and dispose the context ASAP. This is considered by MANY people an acceptable practice. While this has the benefits of occupying your db resources for the minimum time it clearly sacrifices all the UnitOfWork and Caching candy EF has to offer.

    Keeping alive a single multipurpose instance of DbContext maximizes the benefits of Caching but since DbContext is not thread safe and each Web request runs on it's own thread, a DbContext per Request is the longest you can keep it.

    So EF's team recommendation about using 1 Db Context per request it's clearly based on the fact that in a Web Application a UnitOfWork most likely is going to be within one request and that request has one thread. So one DbContext per request is like the ideal benefit of UnitOfWork and Caching.

    But in many cases this is not true. I consider Logging a separate UnitOfWork thus having a new DbContext for Post-Request Logging in async threads is completely acceptable

    So Finally it turns down that a DbContext's lifetime is restricted to these two parameters. UnitOfWork and Thread

    0 讨论(0)
  • 2020-11-21 22:52

    I'm pretty certain it is because the DbContext is not at all thread safe. So sharing the thing is never a good idea.

    0 讨论(0)
  • 2020-11-21 22:54

    Another understated reason for not using a singleton DbContext, even in a single threaded single user application, is because of the identity map pattern it uses. It means that every time you retrieve data using query or by id, it will keep the retrieved entity instances in cache. The next time you retrieve the same entity, it will give you the cached instance of the entity, if available, with any modifications you have done in the same session. This is necessary so the SaveChanges method does not end up with multiple different entity instances of the same database record(s); otherwise, the context would have to somehow merge the data from all those entity instances.

    The reason that is a problem is a singleton DbContext can become a time bomb that could eventually cache the whole database + the overhead of .NET objects in memory.

    There are ways around this behavior by only using Linq queries with the .NoTracking() extension method. Also these days PCs have a lot of RAM. But usually that is not the desired behavior.

    0 讨论(0)
提交回复
热议问题