這段時間在項目中運用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行代碼中,我們發現
【編后語】
博文發表后,筆者看到許多同行的討論,也學到不少東西。筆者這里申明,自己並非黑EF。正是看到了EF的強大,才開始使用它。但是,任何東西都要學懂。就好比筆者來說,之前的用法,導致一個10行的列表,數據庫查詢就執行了好幾百次。雖然這是在開發中問題不是很明顯,但是一旦正式使用,這絕對是一個隱藏的炸彈。
EF很強大,但是,不懂它心的人,只會讓自己傷心!
與廣大程序員共勉!
【再編后語】
筆者很不平。多數同行並沒有看懂這篇博文的意思。循環2次是模擬展示列表。例如,我有表格要展示這個客戶的訂單,沒有分頁。按照前面那種方案
customer.Orders.FirstOrDefault();
這個客戶有多少可訂單,數據庫執行多少次。
而第二種方案:
var query = db.Customers.Include("Orders").ToList();
數據庫只執行一次就可以。展示表格的時候都是從內存中獲取。
這就是是個明顯數據庫性能的問題。筆者虛心向大家討教!