使用Linq to Entities进行分组和多重排序

6

我可以为您翻译以下内容,这是两个实体:

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; }
}

现在我需要按课程分组并按(降序)课程开始日期,然后按班级开始时间排序。我可以按课程分组并按Course.StartDate排序:
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()

但是班级永远不会按照它们的开始时间排序。

*更好的解释: 这是一个 WebService,我必须返回一个 List<IGrouping<Course, Class>>,我真的希望有一种优雅的方式来做到这一点(即只使用 Linq),而不是手动创建列表。当然,除非不可能。

任何帮助都将不胜感激(顺便说一句,我正在使用 EF code-first 4.3)。

3个回答

2

看起来你确实需要 列表的列表,其中每个嵌套列表都按你提到的附加条件排序。由于你正在按 Course 进行分组,所以可以按开始日期对 分组 进行排序,因为组内的每个条目都具有相同的开始日期。然后,你可以将 已分组的元素 投影到按开始时间排序的列表中:

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

问题是我真的需要返回一个 List<IGrouping<Course, Class>>。这是一个 WebService,我无法修改消费者。否则,我只需在客户端上执行 foreach (var class in group.OrderBy(c => c.StartTime),但很遗憾我不能这样做。 - Henrique Miranda

2

如果您需要保留签名,我建议采取以下措施:

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在维护找到的顺序方面的行为,可能会受到更改的影响。

这可能是你最好的选择。ToLookup 返回一个 ILookup<TKey, TElement>,它实现了 IEnumerable<IGrouping<TKey, TElement>>,这意味着你可以将其传递给 Web 服务(你可能需要进行类型转换,但这不应该是问题)。 - SPFiredrake
我用一个不太正规的解决方法解决了它,但这个方法更加优雅。 - Henrique Miranda

1

由于 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()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接