2017-04-04 91 views
2

我發現了與使用.Include等進行加載有關的各種問題,但它好像是SQL形狀查詢到一個巨大的聯接,這意味着如果我得到客戶信息以及客戶擁有1000個項目,我做實體框架 - 爲集合中的成員加載特定導航屬性

context.Customers.Include(c=> c.Inventory).Where(c=>c.ID == id); 
//side note, WHY can't I use .Find() after Include? 

我會得到1000行與項目信息,而不是1條的客戶記錄,並在多臺套1000項一起重複相同的客戶信息。這看起來效率很低,會導致一些非常慢的查詢。

所以,如果我有一組客戶:

//get all customers in TX 
var texasCustomers = context.Customers.Where(c=> c.State == "TX"); 

我要循環他們出口到XLSX:

foreach (var c in texasCustomers) { 
    //compile row per item SKU 
    foreach(var sku in c.Inventory.GroupBy(i=>i.SKU)) { 
    xlsx.SetCellValue(row, col, c.Name); 
    //output more customer info here 
    xlsx.SetCellValue(row, col, sku.Key); 
    xlsx.SetCellValue(row, col, sku.Sum(i=>i.Quantity)); 
    } 
} 

這生成一個查詢到「清單」表PER客戶。這是一個快速查詢,但是當您執行1000次SAME查詢時,它會變得非常慢。

這樣所以我所做的事情:

//get customer key 
var customerids = texasCustomers.Select(c=> c.ID).ToArray(); 
var inventories = context.Inventories.Where(i=> customerids.Contains(i.CustomerID)).ToList(); 

...現在我出口環看起來更像這個...內環從第一個例子中的導航性能運行變爲同對清單對象的預建列表-Memory LINQ過濾器:

foreach (var c in texasCustomers) { 
    //compile row per item SKU 
    foreach(var sku in inventories.Where(i=> i.CustomerID == c.ID)) { 
    xlsx.SetCellValue(row, col, c.Name); 
    //output more customer info and then the sku info 
    xlsx.SetCellValue(row, col, sku.Key); 
    xlsx.SetCellValue(row, col, sku.Sum(i=>i.Quantity)); 
    } 
} 

這成功地得到周圍發出「每循環查詢」,但有明顯的缺點......,只是覺得不妥。

那麼,我錯過了什麼?那是哪裏讓我做類似的祕密EF特點:

texasCustomers.LoadAll(c=> c.Inventories); 

爲「填充」所有集合成員的導航性能的一個去了?還是我從錯誤的角度來處理問題?

有沒有一種方法來構造查詢來獲得EF來生成不會變成單個巨型非規格化表的SQL?

回答

2

沒有祕密EF功能,讓你做你想要什麼,但有一些接近所謂導航屬性修復了,其中填充物化實體的導航性能,即使沒有Include如果相關的實體是已經在上下文中加載。

因此,你可以先加載相關清單如下:

texasCustomers.SelectMany(c => c.Inventories).Load(); 

,然後執行和遍歷主查詢:訪問導航屬性時

foreach (var c in texasCustomers) 
{ 
    var inventories = c.Inventories; // must be there 
    // ... 
} 

但是爲了避免延遲加載,使確保在完成上述所有操作之前先禁用延遲加載,方法是在最開始插入以下行:

context.Configuration.ProxyCreationEnabled = false; 

我忘記提到的一個重要細節是,使用上述技術,如果沒有相關實體,導航屬性將保留null而不是像正常用法那樣返回空列表,因此請確保檢查包括null檢查或使用?? Enumerable.Empty<Inventory>()訪問它時。

+0

啊,這感覺好多了。去給這個鏡頭。謝謝。 – BLSully

+0

優秀信息!這讓我有90%的途徑。似乎是我還不明白的一些方面(例如:不是從'.Find()'工作,但如果我給它一個未處理的'.AsQueryable()',它可以工作嗎?不是)但是,我的EF數據庫日誌從4MEGAbytes下降到7KB(4批量查詢與〜1000x3碎片查詢) – BLSully

+0

事實上,它只適用於'IQueryable's。對於Find來說,幾乎沒有任何與加載有關的數據方法不能直接與它一起工作,所以只要忘記它 - 使用簡單的Where或FirstOrDefault等。儘管對於單個實體,您可以以任何方式獲得它,包括找到',然後使用導航集合屬性的[顯式加載](https://msdn.microsoft.com/en-us/library/jj574232(v = vs.113).aspx#Anchor_2)。順便說一句,在我的答案中使用的技術是**的應用過濾器,當明確加載鏈接中的相關實體**部分。 –

0

其實我認爲你應該把它分開。

客戶另一個訂單和OrderItens的另一個表。

訂單將Ë頭部和OrderItens體(像這樣)

,並使其之間的關係。

之後,您應該將整個孩子加載到內存中並對其應用First()函數。