EF6學習筆記十四:上下文管理

要專業系統地學習EF前往《你必須掌握的Entity Framework 6.x與Core 2.0》這本書的做者(汪鵬,Jeffcky)的博客:https://www.cnblogs.com/CreateMyself/

前面學的東西只算入門,爲了理解EF,書中的EF進階非學不可,我看的很吃力。在百度上看關於EF上下文的文章,不多,讓人很無助。html

我反覆看了幾遍,仍是有不少東西不理解,沒辦法了,那些東西真的沒有什麼接觸,想象不出來。如今只能努力用本身的話複述一遍,並記錄一些本身的問題,主要是想記錄本身的思考的過程。web

使用EF咱們要本身寫一個派生自DbContext的上下文類,這是咱們與數據庫交互的橋樑。數據庫

我對這個類瞭解多嗎?其實不多。在EF的最第一版本是利用ObjectContext來進行數據訪問的,如今的DbContext底層仍是基於ObjectContext,通過改造後的DbContext,性能更好,官方推薦使用DbContext。若是咱們想用ObjectContext也沒問題,由於他們之間能夠互相轉換。緩存

  DbContext 轉 ObjectContext安全

using (EFDbContext ctx = new EFDbContext()) { var objCtx = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)ctx).ObjectContext; } 
View Code

  ObjectContext 轉 DbContext多線程

System.Data.Entity.Core.Objects.ObjectContext objCtx = new System.Data.Entity.Core.Objects.ObjectContext("connectionStr"); var ctx = new System.Data.Entity.DbContext(objCtx,true);
View Code

如今來講我理解的一些上下文管理的東西。併發

上下文生命週期管理

上下文我知道,不過也是後來才知道。當一個東西,不給出上下文你是不能很好的理解他的,由於沒有約束,範圍太大,對於不一樣的環境有不一樣的理解。和說話同樣,對於一句話你還要給出這句話的語境,別人才能真正知道。異步

當我說出一句「真香」,你知道我說的是真的香仍是真香定律?這和泛型約束同樣,咱們對類型添加的那些約束也能夠看作是這個類型的上下文。async

我最開始弄EF,一樣的是派生一個上下文,可是那時我給上下文取的變量名是「db」,由於我就認爲它是一個虛擬的數據庫。可是以爲不許確,並且直到我無心中看到上下文中有個屬性叫Database……分佈式

 

若是上下文就是數據庫,那麼它怎麼還要有一個Database屬性呢?確定不是這樣的,Database應該看作是EF這個語境中的一句話,這個大語境中還有配置、追蹤……這些內容,因此要理解整個語境才能理解一句話。弄懂上下文和他裏面database的關係。

生命週期,什麼意思,按照字面意思來講,一我的從出生到死亡的過程就是這我的的生命週期,一個手機生產到使用報廢的過程,再怎麼玄之又玄的解釋確定不能懷疑不能動搖,它就是一個出生到死亡的過程。

我這說了好像等於沒說啊,其實,我想要對本身強調一遍。根本性的東西不能動搖。既然它叫了這個名字,那麼若是實際狀況不是這樣,只能說明是它的問題不是個人問題。

由於概念性的東西總是聽別人說來講去,一個這樣說,另外一個那樣說,若是你不去繼續研究,他對你就是一個模糊的、說不出來的東西,並且會懷疑他的命名。

上下文生命週期管理,說的就是……上下文生命週期管理

上下文的生命週期開始於初始化,結束於被垃圾回收器回收並釋放。釋放?你釋放就是了,你不是內存託管嗎?

原來,要是你不使用using來包括上下文,就要本身手動釋放資源。

但是你不是內存託管嗎?我本身建立一個Studen類,也歷來不用去手動釋放啊

在之前寫ado的時候我記得要建立數據庫鏈接,用try/catch包裹起來,最後在finally中調用close方法關閉鏈接。

後來我知道了託管代碼和非託管代碼這一個東西。個人理解就是隻要你不寫指針不本身操做內存,那麼就是寫的託管代碼。

因此我認爲是否是有些東西不能託管,百度了一下果真:

系統的資源包括託管資源和非託管資源。例如文件流,數據庫的鏈接,系統的窗口句柄,打印機資源等等這些是非託管資源。」 https://www.cnblogs.com/enamorbreeze/p/4711468.html

那就必定是上下文中使用了非託管資源了,難道是上下文對象不銷燬,那麼由它建立的數據庫鏈接就不會關閉?應該不是,由於我在前面弄EF時候看過EF生成的SQL和SQL的執行狀況

好比我根據導航屬性訂單來查詢產品,訂單和產品一對多的關係

var res = ctx.Products.Where(x => x.Order != null).ToList(); Console.WriteLine(JsonConvert.SerializeObject(res, set));

SQL的執行狀況是這樣

能夠看到EF每執行一次SQL,就會打開/關閉一次數據庫鏈接

那就說明他本身有關閉數據庫鏈接的能力啊,並非個人上下文對象銷燬了,數據庫鏈接才關閉。

可是總的來講上下文你不使用using來包裹,就須要手動釋放資源,確定是用到了哪些非託管資源。

如何更乾淨地使用上下文

 我一開始使用EF,實例化上下文和普通對象是同樣的寫法,由於我不知道除非using包裹就要手動釋放資源。

using幾年前我就見到了,一直沒有去弄過,不知道怎麼回事,在寫ado的時候我用的try/catch也看到有人寫using

原來這個using能夠看作就是try/catch的語法糖,using通過反編譯後的本質是建立try/finally塊,並最終在finally中調用Dispose方法釋放。

書中這樣說:"在使用上下文時,始終要謹記一個準則:一個請求對應一個上下文。在項目中上下文全局惟一,這種方式顯然不可取,在多線程web應用程序中性能極低,尤爲對併發操做。"

咱們來看三種上下文使用方式

Using Pattern  使用using,這種方式的缺點,控制器中的方法可能用到上下文的狀況很是多,這樣就會處處充斥這些using

using(EFDbContext ctx = new EFDbContext()) { }
View Code

DisPose Pattern   上下文和控制器一同建立和銷燬

public class TestController : Controller { private EFDbContext _ctx; public TestController() { _ctx = new EFDbContext(); } protected override void Dispose(bool disposing) { if (disposing) { _ctx.Dispose(); } base.Dispose(disposing); } }
View Code

 Dependency Injection  依賴注入的方式是最被開發者推薦的模式

public class TestController : Controller { private EFDbContext _ctx; public TestController(EFDbContext ctx) { _ctx = ctx; } }
View Code

 如今上下文就不是由控制器來建立和銷燬了,並且被注入進來的,控制器只管使用就好了。我對依賴注入沒有什麼認識,一直沒有去弄。注入明白,依賴呢?難道是這個被注入的對象和控制器的構造函數綁定在一塊兒了,兩者缺一不可,造成了依賴關係?

行吧,書中講到了生命週期追溯,這裏我就實在看不明白了,由於缺乏經驗,徹底想象不出那種多數據庫,分佈式系統多線程和並行操做那種對我來講很陌生的環境,因此,這裏我就不說了。

做者說上下文派生實例並不是類線程安全,所以咱們不能再多線程中訪問上下文實例,這會形成屢次查詢經過相同的數據庫鏈接同時併發返回,也會破壞經過上下文維護實體的變動追蹤和工做單元的一級緩存,因此在多線程應用程序中必須爲每一個線程使用獨立的上下文派生實例。

所以EF6.x引入了異步查詢功能,異步查詢只支持在同一時刻查詢一次,超過一次就會拋出異常

來看一下

var res1 = ctx.Products.ToListAsync(); var res2 = ctx.Products.ToListAsync();
View Code 

這樣直接報錯,而且VS崩潰

須要使用await來等待上一次查詢完成,再接着執行下一步的查詢

public async Task<string> Test() { JsonSerializerSettings set = new JsonSerializerSettings(); set.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; string result = string.Empty; using (EFDbContext ctx = new EFDbContext()) { var res1 = await ctx.Products.ToListAsync(); var res2 = await ctx.Products.ToListAsync(); var res3 = res1.Concat(res2); result = JsonConvert.SerializeObject(res3,set); } return result; } Task<string> res = new Program2().Test(); Console.WriteLine(res.Result);
View Code

 

 行了,就這吧。弄明白了一些,也有不少沒弄明白,路漫修遠兮。

 

原文出處:https://www.cnblogs.com/jinshan-go/p/10293396.html

相關文章
相關標籤/搜索