简体   繁体   English

使用LINQ对实体对多个级别的子集合进行排序

[英]Sorting Multiple Levels of Child Collections with LINQ to Entities

Consider the following model: 考虑以下模型:

public class Form
{
    public Guid Id
    {
        get;

        set;
    }

    public List<Section> Sections
    {
        get;

        set;
    }
}

public class Section
{
    public Guid Id
    {
        get;

        set;
    }

    public List<Question> Questions
    {
        get;

        set;
    }

    public int SortOrder
    {
        get;

        set;
    }
}

public class Question
{
    public Guid Id
    {
        get;

        set;
    }

    public int SortOrder
    {
        get;

        set;
    }
}

When I retrieve one or more Form objects using LINQ to Entities, I would like to have the associated collection of Section objects sorted by the SortOrder property. 当我使用LINQ to Entities检索一个或多个Form对象时,我希望通过SortOrder属性对Section对象的关联集合进行排序。 Further, within each of these Section objects, I would like to have the associated collection of Question objects sorted in the same fashion. 此外,在每个这些Section对象中,我想以相同的方式对相关的Question对象集合进行排序。

I don't recall where I read it, but I have been able to get the first-level sort to work using a LINQ query similar to the following: 我不记得我在哪里读它,但是我已经能够使用类似于以下内容的LINQ查询来使第一级排序工作:

var query =
    from
        f in context.Form
    where
        f.Id == *some form id*
    select
        new
        {
            Root = f,
            Sections = f.Sections.OrderBy(s => s.SortOrder)
        };

From there, I could get the actual Form object by using something like: 从那里,我可以使用类似的方法来获取实际的Form对象:

var form = query.ToList().Select(q => q.Root).FirstOrDefault();

What I cannot figure out is how to write the LINQ query to extend this behavior down to the second-level collection (the collection of Question objects within each Section object. 我无法弄清楚的是如何编写LINQ查询以将此行为扩展到第二级集合(每个Section对象中的Question对象的集合)。

* UPDATE * *更新*

See my comment to Ivan below which explains how this question is not a duplicate. 请参阅下面我对Ivan的评论,该评论解释了此问题不是重复的。

To achieve what you need you can use Eager Loading : 要实现您所需要的,可以使用Eager Loading

var query= from f in context.Form.Include(f=>f.Sections.Select(s=>s.Questions))
           where f.Id == *some form id*
           select
                  new
                     {
                        Root = f,
                        Sections = f.Sections
                                    .OrderBy(s => s.SortOrder)
                                    .Select(s=> new{s.Id, s.SortOrder, Questions=s.Questions.OrderBy(q=>q.SortOrder)})
                     };

Include extension method allows you include as part of your query related entities, even deep levels (check the Remarks section in the link I quoted above). Include扩展方法使您甚至可以将与查询相关的实体包括在内,甚至包括更深层次的内容(请查看我在上面引用的链接中的“ 备注”部分)。

A second solution could be using Lazy Loading , if you haven't disabled this feature, which is enable by default, you need to meet some requirements to be used, for example, your navigation properties must be virtual . 第二种解决方案可能是使用延迟加载 ,如果您尚未禁用此功能(默认情况下处于启用状态),则需要满足一些要使用的要求 ,例如,导航属性必须为virtual

Update 更新资料

You can also sort your navigation properties in memory like the solution in the post that was quoted by @IvanStoev, but if you want to bring the related entities in some order, filtered, among other operations, you could consider use Explicit Loading : 您也可以像@IvanStoev引用的帖子中的解决方案一样对内存中的导航属性进行排序,但是,如果要将相关实体按某种顺序进行过滤,过滤以及其他操作,则可以考虑使用Explicit Loading

foreach f in context.Form
{
   context.Entry(f).Collection(r => r.Sections)
          .Query().OrderBy(s=>s.SortOrder)
          .Load();
}

But IMHO the best solution is create custom classes (also called DTOs ) to project the desired result, to load only the data that you need in one round trip 但是恕我直言,最好的解决方案是创建自定义类(也称为DTO )以投影所需的结果,仅在一次往返中加载所需的数据

Having in mind you are directly querying the whole set of tables, then you have no need for Eager Loading with the method .Include() . 考虑到您直接查询整个表集,那么就不需要使用.Include()方法来进行.Include() Eager Loading

Here is a lambda expression way of solving this with explictly mapping properties/columns. 这是通过显式映射属性/列来解决此问题的lambda表示方式。

// TODO: replace the null value with a real context DbSet<Form>
        IQueryable<Form> forms = null;

        var form = forms.Select(x => new Form()
        {
            Id = x.Id,
            Sections = x.Sections.OrderBy(s => s.SortOrder).Select(s => new Section()
            {
                Id = s.Id,
                SortOrder = s.SortOrder,
                Questions = s.Questions.OrderBy(q => q.SortOrder).Select(q => new Question()
                {
                    Id = q.Id,
                    SortOrder = q.SortOrder
                }).ToList()
            }).ToList()
        }).FirstOrDefault();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM