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(線程中的靜態),因爲我們無法控制線程的生命週期。