.NET實體框架和事務

.net entity-framework sql-server-2005

作為實體框架的新手,我真的很痴迷於如何處理這一系列問題。在我目前正在進行的項目中,整個站點與EF模型高度集成。首先,使用依賴注入引導程序控制對EF上下文的訪問。出於操作原因,我們無法使用DI庫。我刪除了它並在需要時使用了上下文對象的各個實例的模型。我開始得到以下異常:

“XXX”類型已多次映射。

我們得出結論,背景的不同實例導致了這個問題。然後,我將上下文對象抽象為一個靜態實例,每個線程/頁面都訪問該實例。我現在得到關於交易的幾個例外之一:

不允許新事務,因為會話中正在運行其他線程。

無法執行事務操作,因為存在處理此事務的待處理請求。

當分配給命令的連接處於掛起的本地事務中時,ExecuteReader要求該命令具有事務。該命令的Transaction屬性尚未初始化。

最後一個異常發生在加載操作上。我沒有嘗試將上下文狀態保存回失敗的線程上的Db。然而,有另一個線程執行這樣的操作。

這些例外情況最多是間歇性的,但我設法讓網站進入一個由於事務鎖定而拒絕新連接的狀態。不幸的是我找不到異常細節。

我想我的第一個問題是,EF模型是否應該從靜態單個實例中使用?此外,是否可以消除EF中的交易需求?我嘗試過使用TransactionScope對象但沒有成功......

說實話,我在這裡很多,並且無法理解為什麼(應該是什麼)相當簡單的操作導致這樣的問題......

一般承認的答案

在Web應用程序中創建全局ObjectContext非常糟糕。 ObjectContext類不是線程安全的。它圍繞工作單元的概念構建,這意味著您使用它來操作單個用例:因此用於業務事務。它旨在處理一個單一的請求。

您遇到的異常是因為您為每個請求創建了一個新事務,但嘗試使用相同的ObjectContext 。你很幸運, ObjectContext檢測到這一點並拋出異常,因為現在你發現這不起作用。

請想一想為什麼這不起作用。 ObjectContext包含數據庫中實體的本地緩存。它允許您進行一系列更改,最後將這些更改提交到數據庫。當使用單個靜態ObjectContext ,多個用戶在該對像上調用SaveChanges ,它應該如何知道究竟應該提交什麼以及什麼不應該提交?因為它不知道,它將保存所有更改,但此時另一個用戶可能仍在進行更改。當您幸運時,EF或您的數據庫將失敗,因為實體處於無效狀態。如果您處於無效狀態的不幸對像已成功保存到數據庫中,並且您可能會在數週後發現數據庫中充滿了垃圾。您的問題的解決方案是為每個請求創建至少一個ObjectContext 。理論上你可以在用戶會話中緩存一個對像上下文,這也是一個壞主意,因為ObjectContext通常會活得太長並且會包含陳舊的數據(因為它的內部緩存不會自動刷新)。

更新

另請注意,每個線程有一個ObjectContext與完整Web應用程序只有一個實例一樣糟糕。 ASP.NET使用線程池,這意味著在Web應用程序的生命週期中將創建有限數量的線程。這基本上意味著那些ObjectContext實例在這種情況下仍然會在應用程序的生命週期中存在,從而導致數據陳舊性的相同問題。

您可能認為每個線程有一個DbContext實際上是線程安全的,但通常情況並非如此,因為ASP.NET有一個異步模型,允許在不同的線程上完成請求而不是它的啟動(以及最新版本的MVC和Web API甚至允許任意數量的線程按順序處理單個請求。這意味著啟動請求並創建ObjectContext的線程可以在初始請求完成之前很久就可以處理另一個請求。但是,該請求中使用的對象(例如網頁,控制器或任何業務類)仍可能引用該ObjectContext 。由於新的Web請求在同一個線程中運行,因此它將獲得與舊請求所使用的相同的ObjectContext實例。這再次導致應用程序中的競爭條件,並導致與一個全局ObjectContext實例導致的相同的線程安全問題。


熱門答案

當您在問題中引用“網站”時,我認為這是一個Web應用程序。靜態成員僅對整個應用程序存在一次,如果您在整個應用程序中使用單個類型模式和單個上下文實例,則所有類型的請求將在整個應用程序中處於各種狀態。

單個靜態上下文實例將不起作用,但每個線程的多個上下文實例將是麻煩的,並且您不能混合和匹配上下文。你需要的是每個線程一個上下文。我們在應用程序中使用依賴注入類型模式完成了此操作。我們的BLL和DAL類將上下文作為方法中的參數,這樣您就可以執行以下操作:

using (TransactionScope ts = new TransactionScope())
{
    using (ObjectContext oContext = new ObjectContext("MyConnection"))
    {
        oBLLClass.Update(oEntity, oContext);
    }
}

如果您需要在更新中調用其他BLL / DAL方法(或您選擇的任何方法),您只需傳遞相同的上下文。這樣,更新/插入/刪除是原子的,單個方法中的eveything使用相同的上下文實例,但該實例未被其他線程使用。



Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因