使用包含EF 4.1 Code-First的Include和/或Select方法时的订单导航属性?

jav*_*iry 8 entity-framework parent-child ef-code-first entity-framework-4.1

这是这里解释的问题的第二步:EF 4.1代码优先:如何加载相关数据(父子孙子)?.在@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();
    }
}
Run Code Online (Sandbox Code Playgroud)

虽然这种方法有效,但它会向数据库发送许多查询,而我正在寻找一种方法,只需一个查询即可完成所有操作.在@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();
Run Code Online (Sandbox Code Playgroud)

现在,我如何在选择儿童(和孙子女)时订购(如上面第一个代码示例所示)?

Sla*_*uma 23

不幸的是,eager loading(Include)不支持对加载的子集合进行任何过滤或排序.有三种方法可以实现您的目标:

  • 使用明确排序的加载对数据库进行多次往返.这是您问题中的第一个代码段.请注意,多次往返不一定是坏的,Include嵌套Include 可以导致数据库和客户端之间传输数据的大量增加.

  • 使用eager加载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();
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 使用投影:

    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();
    
    Run Code Online (Sandbox Code Playgroud)

这只是一次往返,如果您不禁用更改跟踪(.AsNoTracking()在此查询中不使用),则可以使用.必须将此投影中的所有对象加载到上下文中(第一个ToList()必要的原因),并且上下文将正确地将导航属性绑定在一起(这是一个称为"关系范围"的功能).