2016-04-15 58 views
0

我一直在測試實體框架,以便更好地理解它,並瞭解它如何有效地用作查詢數據庫的後端設備。提高實體框架查詢的性能

作爲參考,我知道實體框架默認使用延遲加載。對於我試圖創建的後端系統,這是沒有用的。

int x = 0; 
using (SandboxContext dbc = new SandboxContext()) { 
    var customers = (from c in dbc.Customer orderby c.AcctNumber select new { c.CustomerKey, c.AcctNumber }).ToList(); 
    var products = (from p in dbc.Product orderby p.CustomerKey select new { p.CustomerKey }).ToList(); 
    foreach (var c in customers) 
     foreach (var p in products.Where(s => s.CustomerKey == c.CustomerKey)) 
      ++x; 
    dbc.Dispose(); 
} 
return x; 

這是相當於我目前使用的代碼。

我試過的一切似乎只會惡化該方法的性能。作爲參考,此代碼在我的機器上執行大約5秒鐘,以返回大約22000個自動生成數據的計數。此代碼,在另一方面運行幾乎在瞬間爲相同的結果:

SqlConnection sqlc = new SqlConnection(sqlConnectString); 
SqlDataAdapter sqlda = new SqlDataAdapter("SELECT customerkey, acctnumber FROM customers", sqlc); 

DataTable dtCustomers = new DataTable(), dtProducts = new DataTable(); 
sqlda.Fill(dtCustomers); 
sqlda.SelectCommand.CommandText = "SELECT customerkey FROM product"; 
sqlda.Fill(dtProducts); 
sqlda.Dispose(); 
sqlc.Close(); 

DataView dvCustomers = new DataView(dtCustomers) { Sort = "AcctNumber" }; 
DataView dvProducts = new DataView(dtProducts) { Sort = "CustomerKey" }; 

int x = 0; 
for (int y = 0; y < 1000; y++) 
    foreach (DataRowView drvCustomers in dvCustomers) { 
     DataRowView[] drvaProducts = dvProducts.FindRows(drvCustomers["customerkey"].ToString()); 
     foreach (DataRowView drvProducts in drvaProducts) 
      ++x; 
     } 
return x; 

我遠遠更喜歡實體框架代碼的整潔性和可讀性,但我認爲我失去了一些信息的重要一塊是顯著傷害了我方法的速度。有什麼想法來改進實體框架代碼,以至少接近DataTable/DataView/DataRowView實現的速度?

+0

應該採取什麼'x'值表示? – dotctor

+3

如果您的模型中有所有必需的互惠屬性(即'Customer'包含'Product'集合),那麼您嘗試執行的操作應該與'dbc.Customer.SelectMany(c => c。產品).Count之間的()'。如果你的'客戶'沒有'產品'集合,那麼考慮設置一個......沒有這個,你會錯過許多EF優點。 – spender

+1

首先,EF有一個預熱成本,它建立了用於構建SQL的模型。 https://msdn.microsoft.com/en-us/library/bb896240(v=vs.100).aspx其次,你擊中數據庫兩次。您可以使用未來的查詢來防止這種情況發生。 https://lostechies.com/jimmybogard/2014/03/11/efficient-querying-with-linq-automapper-and-future-queries/ –

回答

3

如果您的上下文設置正確,您應該會發現Customer的集合Product。爲了這個答案,我們稱之爲屬性Products

通過使用Products屬性,您要求EF代表您執行連接,因此您不必再自己顯式編寫連接。事實上,寫出那些「長手」是不必要的冗長的,因爲EF爲你做了這一切,這將是一件很奇怪的事情。

所以,現在,你可以選擇屬於客戶一樣容易產品:

dbc.Customer.Select(c => c.Products) 

,這將有效地讓你的產品列表的列表。

現在,如果我們將列表清單與SelectMany平鋪到列表中,則可以輕鬆對產品列表進行計數。

所以:

using(var dbc = new SandboxContext()) 
{ 
    var customerProductsCount = dbc.Customer 
            .SelectMany(c => c.Products) 
            .Count(); 
} //note, no .Dispose... `using` took care of that for us. 
1

您不應該在using聲明中處理您的上下文,因爲using會爲您處理。

調用ToList將執行查詢並阻止您構建複雜查詢並在數據庫端調用它們。調用ToList將從數據庫中獲取數據並可能顯着降低性能。

當您不需要時,不需要排序查詢的結果。它只會增加開銷並增加執行時間。

最後,你似乎可以減少整個代碼只是一個簡單的查詢(thanks JoaoFSA)。

using (SandboxContext dbc = new SandboxContext()) 
{ 
    return dbc.Customer.Join(dbc.Product, 
       c => c.CustomerKey, 
       p => p.CustomerKey, 
       (c, p) => new { Customer = c, Product = p}) 
       .Count(); 
} 
1

那麼當您使用EF你是在做數據庫排序,但目前使用的是什麼,你做它的代碼,如果我讀它的權利,性能可能會有所不同。

但是我發現你的方法中遇到的最大問題是,你正在將所有客戶和產品從數據庫加載到應用程序中,然後在應用程序中進行加入和計數,如果這是在數據庫中完成的,則性能會好很多像這樣:

using (SandboxContext dbc = new SandboxContext()) { 
    return (from c in dbc.Customer join p in dbc.Product on c.CustomerKey equals p.CustomerKey select p).Count(); } 
+0

作爲一個fyi,(我不知道它是否適用於每個版本,語法對我來說還是比較新的),除非在查詢塊的末尾有一個select或group子句,否則該給定將不會編譯儘管如此,但是)。 – LightToTheEnd

+0

是的,我在飛行中寫道,忘記了。添加 – JoaoFSA

1

你有興趣關閉延遲加載嗎?您可以通過添加

this.Configuration.LazyLoadingEnabled = false; 

在您的SandboxContext上下文的構造函數中。