簡體   English   中英

多線程應用程序中的單個DbContext實例

[英]Single DbContext instance in multi-thread application

在我當前正在使用的MVVM WPF應用程序中,實體框架6的使用方式如下:

  • 代碼優先實體模型
  • 一個DbContext在應用程序的開始處創建並被共享(通常在構造函數中傳遞)
  • 一些實體的某些屬性直接綁定到UI-或未映射並保存與UI相關的內容,例如。 在此類實體的構造函數中創建的UserControls
  • 有一個單獨的線程刷新我們的應用程序要執行的“作業”-這些“作業”由外部應用程序直接寫入數據庫
  • UI線程使用相同的DbContext在UI中的單擊操作上添加,刪除或更改這些“作業”和其他實體
  • 還有另一個單獨的線程用於刷新和管理其他實體
  • 實體之間利用延遲加載的優勢相互鏈接

首先,我們遇到了context.SaveChanges()問題-我們遇到了各種不同的異常,例如:

"New transaction is not allowed because there are other threads running in the session"

"The property "ID" is part of the object's key information and cannot be modified"

"The transaction operation cannot be performed because there are pending requests working on this transaction"

因此,我們在上下文類中為此實現了簡單的鎖定,希望解決此問題:

public override int SaveChanges()
{
    lock (this)
    {
        return base.SaveChanges();
    }
}

這僅部分地有所幫助,因為現在我們遇到了以下異常,這種異常的出現頻率降低了:

"An error occurred while saving entities that do not expose foreign key properties for their relationships"

此外,有時鏈接屬性也會出現問題。 即使它們都被定義為虛擬的以啟用延遲加載,但有時我們會得到一個空引用異常,因為它們不會被鏈接。

我主要關心的是:

  • 經過一些研究,我發現EF的這種實現不是應該的(上下文應該是短暫的)
  • 在模型中具有UI綁定將打破SoC范式
  • dbContext不是線程安全的

我認為理想情況下,我們應該重構體系結構-也許通過開發一些單獨的層來處理這些問題,但這在我們的案例中將是耗時的,而不是首選的解決方案。

是否可以使用DbContext和EF6的方式與我們在應用程序中已經設計的方式相同,並且進行了一些更改以解決問題?

根據您的用例,啟動查詢時,使用.AsNoTracking()擴展方法可能會有所幫助。 這樣一來,DbContext就不會跟蹤對對象屬性的更改,因此,如果一個線程使用.AsNoTracking()來獲取GETS數據,而另一個線程更新一個潛在沖突的數據集(不使用.AsNoTracking()),則這兩個操作不應相互干擾彼此之間。

到目前為止,您對問題的分析是正確的。 DbContexts應該是短暫的。 UI不應該綁定到實體(即使很多示例,甚至是Microsoft都這樣做),並且DbContext當然不是線程安全的。 壞消息是,這不可能是一個簡單的方法。

從一個長壽的DbContext切換到一個短命的DbContext是一件微不足道的事情。 但是,處理可能在DbContext實例之間傳遞的實體引用不是那么簡單。 由於這些實體從域/業務邏輯傳播到視圖並返回,因此這直接混入了與實體綁定的第二個缺陷。 短期生存的上下文在大多數情況下應該可以解決線程安全問題,只要這些實體不在上下文之間移動就可以了。

在成熟的應用程序中解決這樣的問題可能很困難,具體取決於應用程序的成熟程度和時間壓力,但這並非不可能。 關鍵是要確定出現問題最多的應用程序區域,並希望首先解決這些問題。 一個面積很小但使用頻繁的區域將是測試重新構造代碼庫所涉及的領域的理想選擇。 為了避免破壞所有內容,您可以利用有限的上下文定義在應用程序的每個區域拆分DbContext定義,以便對於一組相關視圖,您可以組成視圖模型結構,並從一個新的,短暫的DbContext中填充它。管理檢索這些屏幕所需的所有實體以及所有持久性。 如果應用程序結構傳遞了很多實體,那么困難的部分將是為代碼建立邊界。 在這些邊界處,將需要更改方法簽名,以使原始DbContext中的實體不會流入新代碼,而調用舊代碼可以將視圖模型轉換回原始DbContext范圍內的實體。 最終,盡管您使用的方法將需要反映應用程序的確切結構。 嚴重依賴抽象,泛型等代碼的代碼可能很難重構。

另一種方法是使用Detach / AttachAsNoTracking開始解決分離的實體。 但是在我看來,這可能會導致您陷入更深的復雜性兔子洞,因此對於將其視為可能的解決方案,我會保持謹慎。 如果您正在從可能很大的記錄子集中讀取數據,並且避免了將它們與DbContext關聯的開銷, AsNoTracking是一個不錯的選擇。 通常,DbContext跟蹤的實體越多,針對上下文的操作就越慢。 因此,搜索和報告等操作將從AsNoTracking受益。 但是,在分離/附加實體時,需要在附加之前有意檢查上下文實例以查看它是否尚未跟蹤具有相同PK的實例,並確保另一個DbContext尚未跟蹤提供的實體。 處理分離/附加嵌套實體圖也很麻煩。

流行語錄的變體。 “有些人認為他們可以使用分離的實體可以解決的問題,現在有兩個問題。” :)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM