你可以在单个查询中高效地完成此操作,语法只是有点别扭:
var groups = await db.Parents
.Where(p => p.Id == id)
.Select(p => new
{
P = p,
C = p.Children.OrderBy(c => c.SortIndex)
})
.ToArrayAsync();
var model = groups
.Select(g => g.P)
.FirstOrDefault();
解释
async 注意事项
这里我使用了 async
扩展,你可能也应该使用它们,但如果需要同步查询而不影响有效的子元素排序,则可以去掉 await
/async
。
第一部分
默认情况下,从数据库中获取的所有 EF 对象都是“跟踪”的。此外,EF 的等效于 SQL 的 Select
是围绕匿名对象设计的,你在上面看到我们选择了它们。当创建匿名对象时,分配给 P
和 C
的对象都是被跟踪的,这意味着它们的关系被记录并且它们的状态由 EF 更改跟踪器维护。由于 C
是 P
中的子代列表,即使你没有明确要求它们在匿名对象中相关联,EF 仍然会将它们作为这个子集合加载,因为它在模式中看到了这种关系。
要了解更多信息,你可以将上述内容分成两个单独的查询,在完全不同的 Db 调用中分别加载父对象和子列表。EF 更改跟踪器将注意到并将子节点加载到父对象中。
第二部分
我们已经欺骗 EF 返回了有序子项。现在我们只获取父对象 - 它的子元素仍将以我们想要的顺序附加。
空值和表作为集合
这里存在一个笨拙的 2 步骤,主要是为了遵循最佳实践,处理空值的情况;它需要完成两件事:
将数据库中的内容视为集合,直到最后一刻。
避免 null 异常。
换句话说,最后一部分也可以是:
var model = groups.First().P;
但是如果该对象在数据库中不存在,将导致空引用异常。 C# 6 将引入另一种替代方法,即空属性合并运算符 - 因此在未来,您可以使用以下代码替换最后一部分:
var model = groups.FirstOrDefault()?.P;