我有两个表,父表和子表。子表具有列排序顺序(数值)。由于EF缺少支持IList包含排序顺序而不暴露排序顺序(参见:Entity Framework persisting child collection sort order),我的子类还有一个属性SortOrder,因此我可以使用排序顺序存储子项。
与引用问题的autor相反,我尝试加载始终排序的子项。因此,如果我加载一个我期望的父实例,那么子集合按排序顺序排序。如何使用Code First Fluent API和POCO实现此行为?
提示:不能在子集合上调用.Sort(...)。
答案 0 :(得分:38)
您无法直接实现它,因为EF中的急切或延迟加载都不支持排序或过滤。
您的选择是:
OrderBy
第二个选项可以与显式加载一起使用:
var parent = context.Parents.First(...);
var entry = context.Entry(parent);
entry.Collection(e => e.Children)
.Query()
.OrderBy(c => c.SortOrder)
.Load();
答案 1 :(得分:27)
你可以在一个查询中有效地做到这一点,语法只是尴尬:
var groups = await db.Parents
.Where(p => p.Id == id)
.Select(p => new
{
P = p,
C = p.Children.OrderBy(c => c.SortIndex)
})
.ToArrayAsync();
// Query/db interaction is over, now grab what we wanted from what was fetched
var model = groups
.Select(g => g.P)
.FirstOrDefault();
异步备注
我碰巧在这里使用了async
扩展程序,您可能应该使用这些扩展程序,但如果您需要同步查询而不会损害效率,则可以删除await
/ async
儿童排序。
第一块
默认情况下,从Db获取的所有EF对象都被跟踪。"此外,EF'等效于SQL Select
是围绕匿名对象设计的,您可以在上面选择它们。创建匿名对象时,将跟踪分配给P
和C
的对象,这意味着它们的关系会被记录,并且它们的状态由EF更改跟踪器维护。由于C
是P
中的子项列表,即使您没有要求它们在您的匿名对象中明确相关, EF也会将它们作为此子集合加载,< / em>因为它在架构中看到的关系。
要了解更多信息,您可以将上述内容分解为2个单独的查询,在完全不同的Db调用中仅加载父对象,然后只加载子列表。 EF Change Tracker会注意到并将子项加载到父对象中。
第二块
我们欺骗EF回归有序的孩子。现在我们只抓住Parent对象 - 它的子节点仍然按照我们想要的顺序连接。
Nulls and Tables as Sets
这里有一个尴尬的两步,主要是针对零点的最佳实践;它可以做两件事:
将数据库中的内容视为设置,直到可能的最后一刻。
避免空例外。
换句话说,最后一块可能是:
var model = groups.First().P;
但是如果对象没有出现在数据库中,则会以空引用异常爆炸。 C# 6 will introduce another alternative though, the null property coalescence operator - 所以将来你可以用:
替换最后一个块var model = groups.FirstOrDefault()?.P;
答案 2 :(得分:0)
除了需要订购外,我还需要限制孩子的成绩。我是这样做的:
var transactions = await _context.Transaction
.Include(x => x.User)
.OrderByDescending(x => x.CreatedAt)
.Where(x => x.User.Id == _tenantInfo.UserId)
.Take(10)
.ToListAsync();
var viewmodel = _mapper.Map<UserViewModel>(transactions.First().User);