EF是個工具,用的好了性能就會很好,用的不好性能就會有很大損失。
先從EF的設計思想來講解
EF的初衷是根據緩存中的實體對象,以及實體對象的狀態(刪除、更新、添加)來對數據庫進行操作,這些實體對象、以及對象的狀態都是在一個對象上下文ObjectContext中進行維護的,數據上下文DbContext顯式實現了IObjectContextAdapter接口。DbContext在最后SaveChanges提交的時候,會調用ObjectContext的SaveChanges, 將所有更新保存到數據庫並重置對象上下文中的更改跟蹤。所以DbContext是對ObjectContext進行的二次封裝和完善。
1.EF的初衷是好的,避免對數據庫頻繁的提交,先操作實體對象,直到實體對象不需要再更改了,最后提交的時候再將所有更新保存到數據庫。但是凡事都是相對的,如果維護了大量的實體會使內存消耗很大,所以怎么辦呢,應該盡量避免不必要的緩存實體,譬如純粹的查詢,EF在查詢的時候可以使用AsNoTracking()避免將查詢的實體或者實體序列放在緩存中,這在很大程度可以節省了內存。
2.EF鼓勵將多個操作放在一個數據上下文中,所以有人說采用EF不需要DAL層了,可以直接在業務層包裝using(DbContext=new DbContext()){},然后對數據庫做各種表的操作,這個是沒問題的,但是在開發中,這樣做效率會很低,會寫很多重復代碼,舉個簡單的例子,假如一個條件查詢,如果有DAL層的封裝方法,我就可以在業務層方便的調用,避免每次都去想着怎么去組裝條件進行查詢,所以DAL層對於開發效率來說還是必要的,如果需要DAL層就要考慮數據倉儲類的實現,數據倉儲類該如何實現呢?
3.數據倉儲類,數據倉儲類封裝了通用的針對數據庫的單元操作方法,封裝這些方法中應該避免使用using(DbContext=new DbContext()){}包裝單元操作,EF的設計初衷是什么?避免頻繁的與數據庫進行交互,如果采用這種方式封裝單元操作,一個單元操作使用一個數據上下文,那么EF的性能優勢在哪里?另外采用這種方式封裝,EF的“本地事務”也被破壞了,已經毫無”事務”可言,所以數據倉儲類可以采用一個臨時公用的數據上下文,比如你將數據上下文放在線程對象中,這里的事務我加了一個引號,因為EF在提交的時候根據對象上下文來更新數據庫的,所以最后一次提交之前,實際上並沒有和數據庫交互,數據上下文提交的時候,會進行事務的封裝,如果失敗了,本次提交的所有操作回滾。
4.EF的初衷是想維護一個完整性的對象上下文,但是實際生產中很難做到,比如執行了一個sqlcommand更新了一個字段,但是對象上下文中的該屬性沒有被更新,所以也許會說,采用EF就避免使用sql進行操作了,但是Dbcontext留下了DataBase口子,public int ExecuteSqlCommand(string sql, params object[] parameters);留下了口子,就避免不了會被采用,所以更穩妥的做法是采用DbContextTransaction來控制事務,而不是單純地依靠SaveChanges,另外如果采用了ExecuteSqlCommand,一旦破壞了數據對象的完整性,再進行更新操作的時候很可能會覆蓋掉之前執行的操作,那么怎么辦呢,解決辦法有兩個,1.根據字段進行更新,而不是更新整個實體2.將數據對象分離,重新附加,然后更新,很明顯1的方法性能更好
5.如何Dispose掉Dbcontext?首先要清楚Dispose是干什么的,Dispose是為了釋放資源,不是銷毀對象,銷毀對象是誰干的,銷毀對象是GC干的,GC准備銷毀對象的時候,會檢查對象有么有析構函數,如果有會將這些對象升級暫不銷毀,直到全部執行完析構,在下次回收的時候再銷毀掉,可是Dbcontext沒有析構函數,但是Dbcontext是對ObjectContext的二次封裝和完善,ObjectContext實現了析構,所以如果Dbcontext沒有顯示的去Dispose,GC回收數據上下文對象的時候,會升級該對象,直到所有的析構執行完畢,在下次回收的時候再一並銷毀掉。我們不知道垃圾回收的具體時間,但是顯示的Dispose可以減少Dbcontext對象的生命周期。所以在應用程序中,我們可以在請求結束的最后,執行顯示的Dispose,譬如對於mvc、webapi程序,可以在過濾器中,請求結束的時候執行Dispose操作,對於asp.net可以在basepage中進行Dispose操作。
// // 摘要: // 由可提供 System.Data.Entity.Infrastructure.IObjectContextAdapter.ObjectContext 實例的對象實現的接口。System.Data.Entity.DbContext // 類實現此接口以提供對基礎 ObjectContext 的訪問。 public interface IObjectContextAdapter { // // 摘要: // 獲取對象上下文。 // // 返回結果: // 對象上下文。 ObjectContext ObjectContext { get; } }
// // 摘要: // DbContext 實例表示工作單元和存儲庫模式的組合,可用來查詢數據庫並將更改組合在一起,這些更改稍后將作為一個單元寫回存儲區中。DbContext 在概念上與 // ObjectContext 類似。 public class DbContext : IDisposable, IObjectContextAdapter {
// // 摘要: // 將在此上下文中所做的所有更改保存到基礎數據庫。 // // 返回結果: // 已寫入基礎數據庫的對象的數目。 public virtual int SaveChanges();