Entity framework 实体框架加载具有排序顺序的子集合

Entity framework 实体框架加载具有排序顺序的子集合,entity-framework,ef-code-first,poco,code-first,Entity Framework,Ef Code First,Poco,Code First,我有两个表:父表和子表。子表有一个列排序器(数值)。由于缺少EF对在不公开排序器的情况下保留包含IList的排序顺序的支持(请参见:),我的子类还有一个属性排序器,因此我可以使用排序顺序存储子类 与所引用问题的autor不同,我尝试加载总是排序的孩子。因此,如果我加载一个父实例,我希望子集合按排序顺序排序。如何使用Code First Fluent API和POCO实现这种行为 提示:这不是在子集合上调用.Sort(…)的选项。您无法直接实现它,因为EF中的即时或延迟加载都不支持排序或筛选 你的

我有两个表:父表和子表。子表有一个列排序器(数值)。由于缺少EF对在不公开排序器的情况下保留包含IList的排序顺序的支持(请参见:),我的子类还有一个属性排序器,因此我可以使用排序顺序存储子类

与所引用问题的autor不同,我尝试加载总是排序的孩子。因此,如果我加载一个父实例,我希望子集合按排序顺序排序。如何使用Code First Fluent API和POCO实现这种行为


提示:这不是在子集合上调用.Sort(…)的选项。

您无法直接实现它,因为EF中的即时或延迟加载都不支持排序或筛选

你的选择是:

  • 从数据库加载数据后,对应用程序中的数据进行排序
  • 执行单独的查询以加载子记录。使用单独查询后,可以使用
    OrderBy
第二个选项可用于显式加载:

var parent = context.Parents.First(...);
var entry = context.Entry(parent);
entry.Collection(e => e.Children)
     .Query()
     .OrderBy(c => c.SortOrder)
     .Load();

您可以在单个查询中高效地执行此操作,但语法很难理解:

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
扩展,您可能应该使用它,但是如果您需要同步查询而不影响有效的子排序,您可以去掉
wait
/
async

第一块

默认情况下,从数据库获取的所有EF对象都是“跟踪”的。此外,EF与SQL
Select
等效的对象是围绕匿名对象设计的,您可以看到我们在上面选择了匿名对象。创建匿名对象时,分配给
P
C
的对象都会被跟踪,这意味着它们之间的关系会被记录下来,它们的状态由EF Change Tracker维护。由于
C
P
中的子对象列表,即使您没有要求它们在匿名对象中明确相关,EF还是将它们作为此子集合加载,因为它在模式中看到了关系

要了解更多信息,可以将上述内容分成两个单独的查询,在完全不同的Db调用中只加载父对象,然后加载子列表。EF更改跟踪器将注意到子对象并为您加载到父对象中

第二块

我们骗EF把订好的孩子还给了他们。现在我们只抓取父对象-它的子对象仍将按我们希望的顺序连接

将空值和表格作为集合

这里有一个笨拙的两步,主要是针对空值的最佳实践;它可以做两件事:

  • 将数据库中的内容视为集合,直到可能的最后一刻

  • 避免空异常

换句话说,最后一块可能是:

var model = groups.First().P;
但如果对象不在数据库中,则会出现空引用异常因此,在将来,您可以将最后一块替换为:

var model = groups.FirstOrDefault()?.P;

除了需要点菜,我还需要限制孩子们的结果。我是这样做的:

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);
var transactions=wait\u context.Transaction
.Include(x=>x.User)
.OrderByDescending(x=>x.CreatedAt)
.Where(x=>x.User.Id==\u tenantInfo.UserId)
.Take(10)
.ToListAsync();
var viewmodel=_mapper.Map(transactions.First().User);

别忘了添加:对system.data.entity和system.Linqals使用语句不要忘了空检查父对象这对我很有效!多棒的黑客!经过几个相关的问题,终于有了一个很好的答案!我认为X181应该将此标记为正确答案。我得到以下错误:
ObjectContext实例已被释放,无法再用于需要连接的操作。
我在代码中所做的所有更改都被
FirstOrDefault()
替换为
ToList()
。知道出了什么问题吗?@JoSmo听起来你在这个受限样本之外遇到了问题。您很可能在某个地方(可能在另一个线程中)处理了DbContext,这导致了此代码失败。您的问题最好是关于StackOverflow的新问题,而不是评论中的讨论-发布完整代码,至少包括任何using语句和线程。@vtortola添加了更多解释,试图解释这里发生了什么,希望能有所帮助。有人知道这是否适用于EF7吗?这对我不起作用,我猜这就是原因。