在使用 EF 4.1 Code-First 的 Include 和/或 Select 方法时订购导航属性?
Posted
技术标签:
【中文标题】在使用 EF 4.1 Code-First 的 Include 和/或 Select 方法时订购导航属性?【英文标题】:Order navigation properties when using Include and/or Select methods with EF 4.1 Code-First? 【发布时间】:2011-11-23 06:43:26 【问题描述】:这是此处解释的问题的第二步:EF 4.1 code-first: How to load related data (parent-child-grandchild)?。 在@Slauma的指导下,我用这种方法成功地检索到了数据:
var model = DbContext.SitePages
.Where(p => p.ParentId == null && p.Level == 1)
.OrderBy(p => p.Order) // ordering parent
.ToList();
foreach (var child in model) // loading children
DbContext.Entry(child)
.Collection(t => t.Children)
.Query()
.OrderBy(t => t.Order) // ordering children
.Load();
foreach (var grand in child.Children) // loading grandchildren
DbContext.Entry(grand)
.Collection(t => t.Children)
.Query()
.OrderBy(t => t.Order) // ordering grandchildren
.Load();
虽然这种方法有效,但它会向数据库发送许多查询,我正在寻找一种方法来在一个查询中完成所有这些操作。在@Slauma 的指导下(在上面链接的答案中进行了解释),我将查询更改为这个:
var model2 = DbContext.SitePages
.Where(p => p.ParentId == null && p.Level == 1)
.OrderBy(p => p.Order)
.Include(p => p.Children // Children: how to order theme???
.Select(c => c.Children) // Grandchildren: how to order them???
).ToList();
现在,我如何在选择子(和孙子)时对其进行排序(如上面的第一个代码示例所示)?
【问题讨论】:
看看这个问题:***.com/questions/4156949/…。您正在尝试做的事情称为“急切加载”,显然,您不能将OrderBy
与Include
一起使用。
是的,我知道急切加载,如果您查看第一个代码(由我自己创建),您会看到我在每个级别的每个对象上都使用了 foreach 语句(顶部为孩子,并且,孙子的孩子)与您准备好的链接中解释的相同。但这需要对数据库进行更多查询!我正在寻找一种在一个查询中完成所有操作的方法,而不是更多!
您能否急切地加载整个结构,然后在需要时在视图中进行排序?几乎没有理由在数据访问逻辑中泄露表示逻辑(排序)。
只有一个原因:避免多次循环。这里有很多 foreach
循环,我想避免它们。在查询数据库或视图时,它们之间没有任何区别,我们将拥有:foreach(foreach())
。很抱歉,我无法解释更多):
但我认为最后,我必须这样做。通过Include(Select())
选择所有这些,然后订购它们。再次感谢您的关注。
【参考方案1】:
你试过关注吗?
...
Select(from ch in c.children
orderby ch.property desending
select ch)
...
【讨论】:
不起作用!错误是 包含路径表达式必须引用在类型上定义的导航属性。对引用导航属性使用虚线路径,对集合导航属性使用 Select 运算符。参数名称:路径【参考方案2】:不幸的是,急切加载 (Include
) 不支持对加载的子集合进行任何过滤或排序。有三种选择可以实现您想要的:
通过显式排序加载多次往返数据库。这是您问题中的第一个代码 sn-p 。请注意,多次往返不一定是坏事,Include
和嵌套的Include
can lead to huge multiplication of transfered data between database and client。
通过Include
或Include(....Select(....))
使用预加载,并在加载后对内存中的数据进行排序:
var model2 = DbContext.SitePages
.Where(p => p.ParentId == null && p.Level == 1)
.OrderBy(p => p.Order)
.Include(p => p.Children.Select(c => c.Children))
.ToList();
foreach (var parent in model2)
parent.Children = parent.Children.OrderBy(c => c.Order).ToList();
foreach (var child in parent.Children)
child.Children = child.Children.OrderBy(cc => cc.Order).ToList();
使用投影:
var model2 = DbContext.SitePages
.Where(p => p.ParentId == null && p.Level == 1)
.OrderBy(p => p.Order)
.Select(p => new
Parent = p,
Children = p.Children.OrderBy(c => c.Order)
.Select(c => new
Child = c,
Children = c.Children.OrderBy(cc => cc.Order)
)
)
.ToList() // don't remove that!
.Select(a => a.Parent)
.ToList();
这只是一次往返,如果您不禁用更改跟踪(请勿在此查询中使用.AsNoTracking()
),则可以使用。此投影中的所有对象都必须加载到上下文中(第一个 ToList()
是必需的原因)并且上下文会将导航属性正确地绑定在一起(这是一个称为“关系跨度”的功能 )。
【讨论】:
天啊,特别感谢 Slauma!干得好 :D:D:D 那行得通,我跟踪它,只针对 db (^_^) 发送一个查询,特别感谢亲爱的。我只是将内部的Select
更改为.Select(c => new Parent = c, Children = c.Children. OrderBy(cc => cc.Order) )
。再次感谢。
@ChrisMoschini:你确定ProxyCreationEnabled
很重要吗?我几乎从不使用更改跟踪代理,并且关系跨度仍然有效。
@Slauma 你是对的!我们的代码既禁用了代理,又不跟踪无法进行这种投影的快速只读查询;但是禁用代理就好了,只是 AsNoTracking() 把扳手扔进去了。
另外一件事,只要我正在测试这个 - 排序子列表似乎从 SQL Server 查询长列表要快得多,而不是未定义顺序,即使你对无序列表没问题作为最终结果。
UPD 信息:因为 EF Core 5.0 - 支持过滤和排序Filtered include以上是关于在使用 EF 4.1 Code-First 的 Include 和/或 Select 方法时订购导航属性?的主要内容,如果未能解决你的问题,请参考以下文章
EF 4.1 Code-First 项目上的 MvcMiniProfiler 不分析 SQL