C# 实体框架-加载集合中成员的特定导航属性
我发现了与使用C# 实体框架-加载集合中成员的特定导航属性,c#,entity-framework,linq,linq-to-entities,C#,Entity Framework,Linq,Linq To Entities,我发现了与使用加载相关的各种问题。包括,等等,但似乎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个项目。这
加载相关的各种问题。包括
,等等,但似乎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));
}
}
这将为每个客户生成对“库存”表的查询。这是一个快速的查询,但是当您执行相同的查询1000次时,它会变得非常慢
所以我做过这样的事情:
//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);
要一次性“填充”集合成员的所有导航属性?还是我从错误的角度看待问题
有没有一种方法可以构造查询,使EF生成的SQL不会变成一个巨大的非规范化表?实际上,我认为应该将其分开 一张桌子给客户,另一张桌子给订单,另一张桌子给订单 命令将使头部变硬,使身体变硬(类似于此) 并使两者之间的关系
之后,您应该将整个子对象加载到内存中,并对其应用First()函数 没有秘密EF功能允许您完全执行您想要的操作,但是有一种叫做导航属性修复的功能,它可以填充物化实体的导航属性,即使没有
Include
,如果相关实体已经加载到上下文中
因此,您可以先按如下方式加载相关库存:
texasCustomers.SelectMany(c => c.Inventories).Load();
然后执行并迭代主查询:
foreach (var c in texasCustomers)
{
var inventories = c.Inventories; // must be there
// ...
}
但为了避免在访问navigation属性时延迟加载,请确保在执行上述所有操作之前,通过在最开始处插入以下行来禁用延迟加载:
context.Configuration.ProxyCreationEnabled = false;
我忘记提到的一个重要细节是,使用上述技术,如果没有相关实体,导航属性将保持
null
,而不是像正常用法一样返回空列表,因此确保选中以包括null
检查或使用??Enumerable.Empty()
当访问它时。啊,这感觉好多了。我要试试看。谢谢。好消息!这让我有了90%的机会。似乎它的某些方面我还不了解(例如:不从.Find()
工作,但如果我给它一个.AsQueryable()
,它还没有被处理,它能工作吗?不确定。我现在可以解决这个问题。)但它将我的EF数据库日志从4MEGAbytes导出到7KB(4个批量查询与约1000x3个零碎查询)实际上,它只在IQueryable
s上工作。关于Find
,几乎没有一种加载相关的数据方法不能直接使用它,所以请忘记它-使用简单的Where
或FirstOrDefault
等。尽管对于单个实体,您可以以任何方式获得它,包括Find
,然后使用导航集合属性。顺便说一句,我在回答中使用的技术是在从link.Oops显式加载相关实体部分时应用过滤器的一种变体,用于加载集合的操作符应该是SelectMany
。感谢您的澄清。特别感谢您提供了它的名称(导航属性修复)。这使得谷歌搜索和查找更多的参考资料变得很容易,我已经将这些参考资料添加到书签中阅读。