避免用using包裝DbContext【翻譯】

   EF和EF Core 的DbContext類實現IDisposable接口。所以,不少最佳編程實踐中都建議你將它們放在一個using()塊中。不幸的是,至少在Web應用程序中,這樣作一般不是一個好主意。
   我與許多從.NET Framework遷移到.NET Core和.NET 5的客戶一塊兒工做,其中一些客戶在舊版應用程序中並無使用依賴項注入,或者沒有一直使用它。結果致使他們的DbContext類有大量的實例。這樣作有不少問題,其中最重要的是它致使了緊耦合。
                  「在Web應用程序中,每一個Web請求應該有且只有一個DbContext。」
   若是您遵循上述規則,則一切正常。不然可能會有不少麻煩。好比你會遇到這樣的問題:如何跟蹤實體,或被跟蹤的實體在你認爲應保存時並無保存(稍後對此進行詳細介紹)。尤爲是若是使用異步代碼,你可能會發現DbContext被釋放的意外狀況,這可能須要一些時間來解決。
                  「在ASP.NET或ASP.NET Core中,配置DbContext最理想的方法是經過DI容器。」
   若是讓DI容器(如Autofac)幫你管理DbContext實例及其生命週期,就能夠避免以上全部這些麻煩。若是您還使用倉儲或相似的抽象,請確保它們的生命週期與DbContext的生命週期一致。ASP.NET Core內置的DI容器和Helper能在Scoped 生命週期類型中爲EF Core作出正確的配置。這意味着每一個請求將建立一個新的DbContext實例。且這個請求中,任何想使用DbContext實例的操做都共享同一實例。在請求結束時將其清理並釋放。若是您使用的是Autofac和EF 6,Scoped就是每一個請求只使用一個實例的意思。數據庫

using語句和DbContext

   使用using語句可能的問題:編程

  • 您釋放了一個DbContext,使得一個實體沒法保存
  • 您異步地把DbContext傳遞給另外一個服務,可是在它被(另外一個服務)使用前,就在原始服務中的using塊中將其釋放
  • 當同時當使用構造函數注入並在ConfigureServices 或者 一個Autofac 模塊中添加一行代碼這種正確的的行爲時(即同時使用using和容器管理),整個應用程序範圍內顯得代碼(沒必要要的)重複和混亂。

多個DbContext的問題

   與using語句和DbContext密切相關的問題是:若是有多個DbContext(由於該using語句一般會建立一個新實例)。
                「若是您有多個DbContext嘗試使用相同的實體實例,那麼您將承受巨大的痛苦。」
   考慮這個簡單的示例,它包含了一個Controller和一個service,二者同時在使用dbContext。app

   假設在Index方法中,從數據庫中讀取 starship時,它的name屬性值爲「 Millenium Falcon」。下次訪問Index時,name屬性值是什麼?異步

   SaveChanges()被調用時不執行任何操做。db實例在StarshipService中並未跟蹤該實體。因此名稱將保持不變。
   那麼,咱們能夠這樣解決這個問題。讓咱們附加實體。這是更新後的service:函數

   再次運行。如今Name的值是什麼?spa

   SaveChanges()仍然不執行任何操做。實體的name被更新,該實體依然未跟蹤。
   讓咱們再嘗試一次:翻譯

   如今,它會起做用嗎?你以爲如何?對象

   是的,如今name的值已更新爲「 Millenium Falcon *」,下次是「 Millenium Falcon **」,等等。blog

   要正確地獲得預期的行爲,須要進行大量的嘗試工做。這段代碼是脆弱且重複的,更糟的是,具備隱藏的暫存依賴性。接口

   那麼,MVC5/EF6的舊代碼中要怎麼使用Autofac呢?

安裝Nuget軟件包

   安裝Autofac.MVC5 nuget軟件包。與.NET Framework nuget包不一樣,它不會添加一堆類,但你仍然須要將它們鏈接起來。
   進到global.asax文件中更新Application_Start():

   當你成功運行它,你還能夠把它們放置到一個模塊中,並把它的helper放到在App_Start目錄中。

   這部分代碼中所作的事情有: 

  • 配置Autofac的依賴項容器
  • 設置它,以建立controller
  • 設置它,以建立 ApplicationDbContext
  • 設置它,以建立 StarshipService
  • 使用 InstancePerRequest設置上面兩個
  • 配置MVC5以使用Autofac來解決其依賴性

   這可能也就須要用5分鐘。更新以後還會有混亂的代碼嗎?好了,服務如今看起來像這樣:

   另外,控制器如今看起來像這樣:

   請注意,這兩種類型如今都遵循顯式依賴項原則。同時注意到,在解耦以後的代碼沒徹底沒有new關鍵字,這與顯式依賴原則有關。

                「方法和類應顯式要求(一般經過方法參數或構造函數參數)它們所須要的協做對象才能正常運行。」

   不用對所引用對象的隱式依賴感到驚訝。若是須要引用某個類,應該在構造函數中引入它(注:依賴注入)。若是要將類進行解耦,,則應該聲明一個抽象類(或接口),而不是直接使用new建立實例或者調用靜態方法。

總結

   EF和EF Core能夠爲您節省大量時間,並使你更容易地關注領域模型而不是底層的數據庫問題。可是若是沒有正確使用它們,當你嘗試找出它們出現異常的緣由時,它們也會讓你很頭痛。避免直接實例化和避免using塊都將使您的代碼更容易使用。對於ASP.NET(Core)應用程序,應確保每一個請求有且僅具備一個DbContext實例,而實現此目的的最佳方法是使用諸如Autofac之類的DI容器(或ASP. NET Core內置的 ServiceCollection)。

 

本文翻譯自 https://ardalis.com/avoid-wrapping-dbcontext-in-using/

相關文章
相關標籤/搜索