[英]Entity Framework - Loading specific Navigational Property for members in collection
我发现了与使用.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();
...现在我的导出循环看起来更像这样......从第一个示例操作导航属性的内部循环成为针对预建的清单对象列表的内存中的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?
没有秘密的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>()
访问时?? Enumerable.Empty<Inventory>()
。
实际上我认为你应该让它分开。
一个表为Customer另一个表到Orders,另一个表到OrderItens。
订单将是头部和OrderItens的身体(类似这样)
并建立它之间的关系。
之后,您应该将整个子项加载到内存中并对其应用First()函数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.