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

时间:2017-04-04 18:08:11

标签: c# entity-framework linq linq-to-entities

我发现了与使用.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();

...现在我的导出循环看起来更像这样......在第一个示例的nav属性上运行的内部循环成为针对预建的清单对象列表的内存中的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);

to"填充"所有收集成员的导航属性一气呵成?还是我从错误的角度解决问题?

有没有办法结构化查询以使EF生成不会变成单个巨型非规范化表的SQL?

2 个答案:

答案 0 :(得分:2)

没有秘密的EF功能可以让你完全按照自己的意愿行事,但有一些叫做导航属性修复的东西,它填充了物化实体的导航属性,即使没有{{ 1}}如果相关实体已经在上下文中加载。

因此,您可以先按如下方式加载相关清单:

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

答案 1 :(得分:0)

实际上我认为你应该让它分开。

一个表为客户提供另一个表到Orders,另一个表用于OrderItens。

订单将在头部和OrderItens身体(类似这样)

并建立它之间的关系。

之后,您应该将整个子项加载到内存中并对其应用First()函数。