6

From One DbContext per web request... why?

My understanding is that a DbContext instance should not be shared across concurrent web request, so definitely not across threads. But how about sharing it across non-concurrent web requests?

Due to thread agility (What is the meaning of thread-agility in ASP.Net?), am I right that a thread can handle more than one web request before it dies?

If so, is it safe to dependency inject a DbContext instance for each thread?

Reason for this is I'm using Unity, which does not include per-request lifetime option. From MVC, EF - DataContext singleton instance Per-Web-Request in Unity , I think I could use a custom LifetimeManager; I'm just wondering if it is safe and sufficient to use PerThreadLifetimeManager.

Community
  • 1
  • 1
user1501961
  • 618
  • 7
  • 11

2 Answers2

6

is it safe to dependency inject a DbContext instance for each thread?

It depends. If your idea is to have one DbContext per web request, and the consistency of your application depends on it, having one DbContext per thread is a bad idea, since a single web request can still get multiple DbContext instances. And since ASP.NET pools threads, instances that are cached per thread, will live for the duration of the entire application, which is very bad for a DbContext (as explained here).

On the other hand, you might be able to come up with a caching scheme that ensures that a single DbContext is used for a single web request, and is returned to a pool when the request is finished, so other web requests could pick it up. This is basically the way how connection pooling in .NET works. But since DbContext instances cache data, that data becomes stale very quickly, so even if you are able to come up with a thread-safe solution, your system still behaves in an inconsistent way, since at some seemingly random moments, old data is shown to the user, while in a following request new data is shown.

I think it is possible to clear the DbContext's cache at the beginning of a web request, but that would be basically be the same as creating a new DbContext for that request, but with the downside of much slower performance.

I'm just wondering if it is safe and sufficient to use PerThreadLifetimeManager.

No, it isn't safe because of the reasons described above.

But it is actually quite easy to register a DbContext on a per-web request basis:

container.Register<MyApplicationEntities>(new InjectionFactory(c => {
    var context = (MyApplicationEntities)HttpContext.Current.Items["__dbcontext"];

    if (context == null) {
        context = new MyApplicationEntities();
        HttpContext.Current.Items["__dbcontext"] = context;
    }

    return context;
})); 
Community
  • 1
  • 1
Steven
  • 151,500
  • 20
  • 287
  • 393
  • Thank you! This is indeed a neat solution. However, will the DbContext instance be disposed? Also, you say "a request can still get multiple DbContext instances", does that mean a request can be handled by multiple threads? – user1501961 Nov 01 '13 at 11:49
  • @user1501961: The [question](http://stackoverflow.com/questions/11306888/what-is-the-meaning-of-thread-agility-in-asp-net) you referenced explains that an ASP.NET request can be handled over multiple threads. This never happens simultaniously, it is an asynchronous model; it can finish a request on a different thread than where it was started. And no, the DbContext will not be automatically be disposed like this. You'll have to code this yourself, or switch to a different container. About all other frameworks have built-in support for this. – Steven Nov 01 '13 at 12:55
  • Would using HierarchicalLifetimeManager solve the problem? It seems to me that by using HierarchicalLifetimeManager, it creates a new child container for each web request, and will dispose the instances when the container gets disposed (when the web request finishes). Am I right? – user1501961 Nov 01 '13 at 13:21
  • @user1501961: I don't know. I'm not that experienced with Unity. You'll have to test that. – Steven Nov 01 '13 at 13:31
  • 1
    @user1501961: for a per request lifetime manager one approach is using HierarchicalLifetimeManager along with a child container. Note that you need to create the child container at the beginning of the request and then dispose at the end. There are a few packages that add per request lifetime to Unity such as [Unity bootstrapper for ASP.NET MVC](http://www.nuget.org/packages/Unity.Mvc/) and [Unity.MVC4](https://www.nuget.org/packages/Unity.Mvc4/). – Randy supports Monica Nov 03 '13 at 03:12
  • How does other frameworks like Autofac and StructureMap deals with this issue? – Jonna Dec 19 '13 at 01:43
  • @Jonna: I can only speak for Simple Injector, but it contains a WebRequestLifestyle that caches components in the HttpContext.Items. During application startup, it registers a HttpModule thathooks to the EndRequest event to.ensure cached components are disposed. – Steven Dec 19 '13 at 06:06
1

If you will use multiple DbContext per request you can end up with a multi threaded application with high possibility loosing data integrity. One web request can be viewed as one transaction ; but with PerThreadLifeTimeManager you would have multiple distinct transactions which aren't related but maybe they should. For example posting a form with many data can end up with saving multiple database table and with 2 or more independent context can happen that one insert succeeds but another fails and you could have inconsistent data.

The other important thing is that the ASP.NET infrastructure uses thread pooling so every started thread will be reused after the request has finished and if something went wrong in one request it can affect another one. That is why not recommended to use any Thread-LocalStorage (static in the thread) in threadpool environment because we can not control the threads' lifetime.

Sergey Berezovskiy
  • 215,927
  • 33
  • 392
  • 421
Peter Kiss
  • 9,153
  • 2
  • 21
  • 38