前言
- 在編寫代碼的時候,我遇到了很多關於EntityFramework6的疑問,所以現在就提前把這些問題列出來做一下解答,以便在以后的代碼編寫過程中減少不必要的Bug。
EntityFramework6的延遲加載(Lazy Loading)是怎么一回事?
- 首先EntityFramework6的延遲加載默認是被支持的,可以通過設置context.Configuration.LazyLoadingEnabled = false來禁止。
- 當啟用延遲加載的時候,EntityFramework訪問實體的相關對象時,也就是一對一、一對多的屬性時,EntityFramework會從你定義的實體派生出一個動態對象,然后覆蓋你的子實體集合訪問屬性來實現。所以我們在定義POCO類的時候,會在關系屬性前加上virtual 來修飾。如果不加修飾,就不會啟動延遲加載。
- 延遲加載,就是在查找某個實體時,會把相關聯的實體對象或者集合全部加載出來。
- 啟用延遲加載的情況,執行以下語句
Sql Server Profiler監視情況如下:
從上面的結果可以看出,當我們需要獲取Role對象的時候,EntityFramework會把與之關聯的對象以動態代理對象(DynamicProxy)
的方式加載出來,數據庫也向與之關聯的表執行了多個關聯查詢才得到結果。
2. 禁用延遲加載的情況,執行以下語句
Sql Server Profiler監視情況如下:
從上面的結果可以看出,執行的結果只有Role對象,與之關聯的對象S_Menus為null,S_Users也沒有結果,說明在禁用延遲加載的情況下,
EntityFramework只對當前執行的上下文對象進行查詢。Sql Server Profiler監視情況的結果也是如此,程序只執行了對S_Role表的讀取
EntityFramework6的DbContext 適合單例模式嗎?
- 之前我們在用三層架構的時候,我們通常會把DBHelper類的支持方法設置為靜態方法,這樣我們不需要實例化就可以調用執行方法。因此我在想DbContext我們能不能使用單例模式來進行創建?答案是不可以的。因為在同一個DbContext實例上下文中,如果A正在編輯某條數據,B也正在編輯某條數據,B在編輯后進行SaveChanges操作,就會把A編輯的數據也進行提交。而實際A編輯的數據可能被取消,但是也提交到了數據庫進行更改,這肯定不是我們需要的結果。
- 如果我們創建了多個DbContext的實例,就可能會遇到並發的問題,因為不同的實例可能會對同一條數據進行修改。EntityFramework可以使用樂觀鎖來解決此問題。
EntityFramework6怎樣使用事務?
- EntityFramework的DbContext中的SaveChanges就自帶事務與分布式事務。
- 如果我們的語句是在同一個DbContext的實例進行提交,那么DbContext中的SaveChanges就會默認啟動事務,如果有一條語句執行失敗,那么事務就會回滾。SaveChanges不會執行。
- 如果我們的語句是在不同的DbContext的實例中進行提交,那我們就要采用分布式事務,也就是TransactionScope來開啟。在TransactionScope中的任意一個DbContext的SaveChanges行失敗后,其他的實例的SaveChanges也不會被執行,從而達到了事務控制的目的。
EntityFramework6中有緩存實現嗎?
- 就目前而言,EntityFramework的緩存只是維護其內部的DbContext,也就是常說的一級緩存。如果要對EntityFramework的查詢結果對象進行緩存,我們就需要使用二級緩存來進行。常用的一些分布式應用緩存,如memcached或Redis都可以實現。CodePlex上也提供了一些二級緩存的實例,我們給以通過Nuget直接下載使用,比如下面的這倆個緩存組件:
EntityFramework.Cache https://efcache.codeplex.com/
IQueryable, IEnumerable, IList 傻傻分不清楚?
以下結論引用園友Teddy Li的博客:
- IQueryable和IEnumerable都是延時執行(Deferred Execution)的,而IList是即時執行(Eager Execution)
- IQueryable和IEnumerable在每次執行時都必須連接數據庫讀取,而IList讀取一次后,以后各次都不需連接數據庫。前兩者很容易造成重復讀取,性能低下,並且可能引發數據不一致性
- IQueryable和IEnumerable的區別:IEnumberalb使用的是LINQ to Object方式,它會將AsEnumerable()時對應的所有記錄都先加載到內存,然后在此基礎上再執行后來的Query。
- 基於性能和數據一致性這兩點,我們使用IQueryable時必須謹慎,而在大多數情況下我們應使用IList。當你打算馬上使用查詢后的結果(比如循環作邏輯處理或者填充到一個table/grid中),並且你不介意該查詢會即時執行,使用ToList();當你希望查詢后的結果可以供調用者(Consummer)作后續查詢(比如這是一個"GetAll"的方法),或者你希望該查詢延時執行,使用AsQueryable()