7行代碼看EntityFramework是如何運行


這段時間在項目中運用Entity Framework作為底層數據交互框架。一個字,爽。不僅提高了開發效率,省了很多代碼,而且數據庫也規范了很多。按照網上的一些教程初步學習,然后實際運用了,再結合MVC ,開發一個模塊的增刪改查,那真是一個爽歪歪。但是,隨着項目不斷完善,數據表越來越多,關聯性也越來越復雜,問題也逐漸露出水面。首先最大的問題是數據查詢慢。有個影響點是Linq里的Count(),查閱了網上許多資料,都沒有好的解決方法。這個問題暫時不說,如果那位大師有良策,還不忘賜教。

影響查詢慢主要問題在於數據查詢,說白了不了解EF是如何執行sql查詢的,什么時候進行sql查詢?以什么方式進行sql查詢的?

我做了一個demo,以微軟的Northwind作為數據庫,有個Customers表和Orders表,Orders表里有個字段CustomerID,是Customers表的外鍵。代碼如下:

1 NorthwindEntities db = new NorthwindEntities();
2 var query = db.Customers.AsEnumerable();
3 for (int idx = 0; idx < 2; idx++)
4 {
5      var customer = query.ElementAt(idx);
6       var order = customer.Orders.FirstOrDefault();
7        if (order != null)
8               Console.WriteLine(order.OrderID);
9  }

在跟蹤代碼時,同時將SQL Server Profiler打開。代碼執行到2行,sql跟蹤器並沒有執行sql語句。當執行第5行時,sql跟蹤器有了反應。

sql 是查詢了customers表。再往下執行到第6行時,

根據外鍵customerid 去查詢orders訂單。如此。每循環一次。數據庫就會執行2次查詢。如果有查詢結果有20條,就會有40次查詢,如果關聯的表越多。查詢的次數就會越多。

系統查詢能不慢嗎。

解決方法:

 1             NorthwindEntities db = new NorthwindEntities();
 2             ////取消EF的延遲加載
 3             db.Configuration.LazyLoadingEnabled = false;
 4             ////一次性查詢出customers和Orders數據,並利用ToList()放入到內存中
 5             var query = db.Customers.Include("Orders").ToList();
 6             for (int idx = 0; idx < 2; idx++)
 7             {
 8                 var customer = query.ElementAt(idx);
 9                 var order = customer.Orders.FirstOrDefault();
10                 if (order != null)
11                     Console.WriteLine(order.OrderID);
12             }

取消EF的延遲加載。利用Include()將所需要對象一次性查詢出來。並利用ToList()將數據存入內存中。

這是優化后的sql運行跟蹤。我們會發現,只實現了一次查詢,sql語句用了left join的方式將數據一次性查詢出來。每次循環也只會訪問內存中。

總結:

從這4行代碼中,我們發現

1.AsEnumerable()和 AsQueryable() 是延遲執行,當具體使用對象時才會執行sql;ToList() 在使用時就已經執行,並數據存入內存中。
2.EF 本身默認的是延遲加載,每個關聯的查詢,每次循環的查詢都是要執行數據庫的。取消延遲加載,利用Include方法,可以一次性將所有數據查詢出來。
以上觀點純屬個人經驗總結,歡迎各位大鳥發表看法,小菜向大家學習了。
 

【編后語】

博文發表后,筆者看到許多同行的討論,也學到不少東西。筆者這里申明,自己並非黑EF。正是看到了EF的強大,才開始使用它。但是,任何東西都要學懂。就好比筆者來說,之前的用法,導致一個10行的列表,數據庫查詢就執行了好幾百次。雖然這是在開發中問題不是很明顯,但是一旦正式使用,這絕對是一個隱藏的炸彈。

EF很強大,但是,不懂它心的人,只會讓自己傷心!

與廣大程序員共勉!

【再編后語】

筆者很不平。多數同行並沒有看懂這篇博文的意思。循環2次是模擬展示列表。例如,我有表格要展示這個客戶的訂單,沒有分頁。按照前面那種方案

 customer.Orders.FirstOrDefault();
這個客戶有多少可訂單,數據庫執行多少次。
而第二種方案:
var query = db.Customers.Include("Orders").ToList();
數據庫只執行一次就可以。展示表格的時候都是從內存中獲取。
這就是是個明顯數據庫性能的問題。筆者虛心向大家討教!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM