使用 Linq 到实体进行分组和多个排序

本文关键字:排序 Linq 实体 使用 | 更新日期: 2023-09-27 18:30:45

我有两个实体:

public class Course
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
    public virtual ICollection<Class> Classes { get; set; }
}
public class Class
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime StartTime { get; set; }
    public DateTime EndTime { get; set; } ]
    public Course Course { get; set; }
}

现在我需要按课程列出每个班级分组,并按(降序)课程.开始日期排序,然后按类.开始时间排序。我可以按课程分组并按课程排序.开始日期:

var myList = context.Classes
    .GroupBy(c => c.Course)
    .OrderByDescending(g => g.Key.StartDate).ToList()

但无法按开始时间对每个类进行排序。我试过这个:

var myList = context.Classes
    .OrderBy(c => c.StartTime)
    .GroupBy(c => c.Course)
    .OrderByDescending(g => g.Key.StartDate).ToList()

甚至这个:

var myList = context.Classes
    .GroupBy(c => c.Course)
    .OrderByDescending(g => g.Key.StartDate)
    .ThenBy(g => g.Select(c => c.StartTime)).ToList()

但是类永远不会按它的开始时间排序。

*编辑以更好地澄清:这是针对 Web 服务的,我必须返回一个List<IGrouping<Course, Class>>,我真的想要一种优雅的方式来执行此操作(即仅使用 Linq),而无需手动创建列表。当然,除非不可能。

感谢任何帮助(顺便说一句,我使用的是 EF 代码优先 4.3

使用 Linq 到实体进行分组和多个排序

)。

如果您需要保留签名,我建议如下:

var myList = context.Classes
                    .GroupBy(cc => cc.Course)
                    .OrderByDescending(gg => gg.Key.StartDate)
                    .ToArray() // <- stops EF and starts L2O
                    .SelectMany(gg => gg.OrderBy(cc => cc.StartTime))
                    .ToLookup(cc => cc.Course, cc => cc)
                    .ToList();

这将产生一种签名类型:List<IGrouping<Course, Class>>并按StartTime正确排序。这依赖于ToLookup在维护找到的订单方面的行为,并且可能会发生变化。

看起来你真的需要一个列表列表,其中每个嵌套列表都按照你提到的其他条件进行排序。由于您是按 Course 进行分组的,因此您可以按开始日期对进行排序,因为组中的每个条目都具有相同的开始日期。然后,您可以将分组的元素投影到按开始时间排序的列表:

var myList = context.Classes
    .GroupBy(c => c.Course)
    .OrderByDescending(g => g.Key.StartDate)
    .Select(g => g.OrderBy(c => c.StartTime).ToList())
    .ToList();

由于 EF 创建直接 SQL,因此无法执行包含同一 SQL 中的排序的分组。

我建议使用 EF 在一个 Linq 语句中进行分组,并以 ToList() 结尾。并且使用Linq To Objects(在内存中)作为第二个Linq语句进行排序。

它应该运行得很快,因为对象已经分组了。

所以这样的事情应该有效:

var myList = context.Classes
    .GroupBy(c => c.Course).ToList()
    .OrderByDescending(g => g.Key.StartDate).ToList()