3

One DbContext per web request... why?asp.net mvc 4 - 好的,每个线程共享DbContext?

我的理解是,一个DbContext实例不应该在并发web请求之间共享,所以绝对不能跨线程。 但是,如何跨非并发web请求共享它?

由于线程敏捷What is the meaning of thread-agility in ASP.Net?),我说对了一个线程可以处理多个Web请求之前,它死了吗?

如果是这样,依赖注入每个线程的DbContext实例是否安全?

这样做的原因是我使用Unity,它不包括每个请求生命周期选项。 从MVC, EF - DataContext singleton instance Per-Web-Request in Unity,我想我可以使用自定义的LifetimeManager;我只是想知道使用PerThreadLifetimeManager是否安全。

回答

4

依赖注入每个线程的DbContext实例是否安全?

这取决于。如果您的想法是每个Web请求有一个DbContext,并且应用程序的一致性取决于它,则每个线程有一个DbContext是一个坏主意,因为单个Web请求仍然可以获取多个DbContext实例。 而且由于ASP.NET将线程池化,因此每个线程缓存的实例将在整个应用程序的持续时间内存活,这对于DbContext(如解释here)非常不利。

在另一方面,你也许可以想出确保单个DbContext用于单个Web请求,并在请求完成后,将返回到游泳池的缓存机制,所以其他网站请求可以捡起来。这基本上就是.NET中连接池的工作方式。但是由于DbContext实例会缓存数据,所以数据很快就会过时,所以即使您能够提出线程安全的解决方案,您的系统仍会以不一致的方式运行,因为在某些看似随机的时刻,会显示旧数据给用户,而在下面的请求中显示新的数据。

我认为可以在网络请求开始时清除DbContext的缓存,但这基本上与为该请求创建新的DbContext相同,但是性能下降很多。

我只是想知道是否安全和足够使用PerThreadLifetimeManager。

不,这是因为上述原因而不安全。

但它实际上是很容易的每个Web请求的基础上注册的DbContext:

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; 
})); 
+0

谢谢!这确实是一个很好的解决方案。但是,DbContext实例是否会被丢弃?另外,你说“一个请求仍然可以获得多个DbContext实例”,这是否意味着一个请求可以被多个线程处理? – user1501961

+0

@ user1501961:您引用的[question](http://stackoverflow.com/questions/11306888/what-is-the-meaning-of-thread-agility-in-asp-net)解释了一个ASP.NET请求可以在多个线程上处理。这不会同时发生,它是一个异步模型;它可以在与启动时不同的线程上完成请求。不,DbContext不会像这样自动处理。你必须自己编码,或切换到不同的容器。关于所有其他框架都有内置的支持。 – Steven

+0

会使用HierarchicalLifetimeManager解决问题吗?在我看来,通过使用HierarchicalLifetimeManager,它为每个Web请求创建一个新的子容器,并在容器被处置时(Web请求完成时)处理这些实例。我对吗? – user1501961

1

如果您将为每个请求使用多个DbContext,那么最终可能会出现多线程应用程序,并且很可能会丢失数据完整性。一个Web请求可以被视为一个事务;但使用PerThreadLifeTimeManager,您将拥有多个不相关的交易,这些交易不相关,但可能他们应该。例如,发布带有许多数据的表单最终可能会保存多个数据库表,并且可能会发生两个或更多个独立的上下文,即一个插入成功但另一个插入失败,并且您可能有不一致的数据。

另一个重要的事情是ASP.NET基础结构使用线程池,因此每个启动的线程在请求​​完成后将被重用,并且如果某个请求中出现问题,它可能会影响另一个线程。这就是为什么不推荐在线程池环境中使用任何Thread-LocalStorage(线程中的静态),因为我们无法控制线程的生命周期。