简体   繁体   English

实体框架 - 为集合中的成员加载特定的导航属性

[英]Entity Framework - Loading specific Navigational Property for members in collection

I've found the variety of questions related to loading using .Include , etc, but it seems like SQL shapes that query into a huge join which means if I'm getting Customer information, and that customer owns 1000 items and I do 我发现了与使用.Include等加载相关的各种问题,但似乎SQL形状查询到一个巨大的连接,这意味着如果我收到客户信息,并且该客户拥有1000个项目,我做

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

I'll get 1000 rows of the same customer information duplicated along with the item information rather than 1 customer record and 1000 items in a multi-table set. 我将获得1000行相同的客户信息以及项目信息,而不是1个客户记录和多表集合中的1000个项目。 This seems really inefficient and leads to some really slow queries. 这似乎效率很低,导致一些非常慢的查询。

So if I have a set of customers: 所以,如果我有一组客户:

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

And I want to loop over them in an export to XLSX: 我想在导出到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));
  }
}

This generates a query to the 'Inventory' table PER customer. 这会生成对“库存”表PER客户的查询。 Which is a fast query, but when you do the SAME query 1000 times, it gets annoyingly slow. 哪个是快速查询,但是当您执行1000次SAME查询时,它会变得非常慢。

So I've done things like this: 所以我做过这样的事情:

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

...and now my export loop looks more like this... the inner loop operating on the nav property from the first example becomes an in-memory linq filter against the prebuilt list of inventory objects: ...现在我的导出循环看起来更像这样......从第一个示例操作导航属性的内部循环成为针对预建的清单对象列表的内存中的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));
  }
}

This successfully gets around the 'query per loop' issue, but has obvious downsides... and just feels wrong. 这成功地解决了“每循环查询”问题,但有明显的缺点......并且感觉不对。

So, what am I missing? 那么,我错过了什么? Where's the secret EF features that let me do something like: 哪些秘密EF功能让我做了类似的事情:

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

to "populate" all of the collection member's navigational properties in one go? 一次性“填充”所有集合成员的导航属性? Or am I approaching the problem from the wrong angle? 还是我从错误的角度来解决问题?

Is there a way to struture the query to get EF to generate SQL that doesn't turn into a single giant denormalized table? 有没有办法结构化查询以使EF生成不会变成单个巨型非规范化表的SQL?

There is no secret EF feature that allows you to do exactly what you want, but there is something close called navigation property fix up , which populates the navigation properties of the materialized entity even without Include if the related entities are already loaded in the context. 没有秘密的EF功能允许您完全按照自己的意愿行事,但有一些称为导航属性修复的东西 ,如果相关实体已在上下文中加载,即使没有Include也会填充实体化实体的导航属性。

Hence you can first load the related inventories as follows: 因此,您可以先加载相关库存,如下所示:

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

and then execute and iterate the main query: 然后执行并迭代主查询:

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

But to avoid lazy loading when accessing the navigation property, make sure the lazy loading is disabled before doing all the above by inserting the following line at the very beginning: 但是为了避免在访问导航属性时延迟加载,请确保在执行上述所有操作之前禁用延迟加载,方法是在开头插入以下行:

context.Configuration.ProxyCreationEnabled = false;

One important detail I forgot to mention is that with the aforementioned technique, if there are no related entities, the navigation property will stay null rather than returning empty list as with normal usages, so make sure to check to include null checks or use ?? Enumerable.Empty<Inventory>() 我忘记提到的一个重要细节是,使用上述技术,如果没有相关实体,导航属性将保持为null而不是像正常使用那样返回空列表,因此请务必检查以包含null检查或使用?? Enumerable.Empty<Inventory>() ?? Enumerable.Empty<Inventory>() when accessing it. 访问时?? Enumerable.Empty<Inventory>()

Actually i think that you should make it separated. 实际上我认为你应该让它分开。

One table for Customer another one to Orders and another to OrderItens. 一个表为Customer另一个表到Orders,另一个表到OrderItens。

Orders will e the head and OrderItens the body (something like this) 订单将是头部和OrderItens的身体(类似这样)

and make the relationship between it. 并建立它之间的关系。

After that, you should load entire children into memory and apply First() function on it. 之后,您应该将整个子项加载到内存中并对其应用First()函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM