[英]Why is Group By translating in Order By by Entity Framework?
我在 .NET 7 中使用实体框架。
我有 3 个实体:
ProfessorId
的Course
CourseId
等的Grade
Professor
我想获取所有分配给一位教授并且至少有 1 个等级与之关联的课程,并在Dictionary<string, CourseViewModel>
中过滤它们,其中 string 是学期。
我写了以下 LINQ 查询:
var professorGradedCourses = _dbContext.Courses
.Where(course => course.ProfessorId == professorId && course.Grades.Any())
.Select(course => new CourseViewModel
{
Title = course.Title,
Semester = course.Semester,
})
.GroupBy(course => course.Semester)
.OrderBy(course => course.Key)
.ToDictionary(group => group.Key, group => group.ToList());
当它执行时,我得到一个异常,说它不能被翻译。
如果我删除OrderBy
并仅保留GroupBy
,它将起作用并且 Microsoft SQL 服务器中翻译的 SQL 是:
SELECT [c].[Semester], [c].[Title]
FROM [Courses] AS [c]
WHERE [c].[ProfessorId] = @__professorId_0
AND EXISTS (SELECT 1
FROM [Grades] AS [g]
WHERE [c].[Id] = [g].[CourseId])
ORDER BY [c].[Semester]
如您所见,它无论如何都会添加ORDER BY
,即使我已将其删除并仅保留GroupBy()
。 有人可以解释为什么会这样吗? 如果我想按降序排序怎么办? 同样奇怪的是,如果我删除GroupBy()
并仅保留OrderBy()
并将ToDictionary
替换为ToList
,它会起作用并且会生成完全相同的查询(只是现在我无法在没有进一步操作的情况下真正使用结果)。
GroupBy
分组依据:
对序列的元素进行分组。
GROUP BY
分组依据:
一个 SELECT 语句子句,它将查询结果分成多组行,通常通过对每组执行一个或多个聚合来进行。 SELECT 语句每组返回一行。
它们不等同。 主要区别是 LINQ GroupBy
按键返回一个集合,而 SQL GROUP BY
按键返回一个元素(列)。
如果投影按键询问一个元素,则 EF Core 将 LINQ GroupBy
转换为 SQL GROUP BY
:
// Get the number of course by semester
context
.Courses
.GroupBy(c => c.Semester)
.Select(cs => new { Semester = cs.Key, Count = cs.Count() })
.ToList();
翻译成:
SELECT [c].[Semester], COUNT(*) AS [Count]
FROM [Courses] AS [c]
GROUP BY [c].[Semester]
但是,如果投影询问多个元素,则 EF Core 将 LINQ GroupBy
转换为 SQL ORDER BY
并自行分组。
context
.Courses
.Select(c => new { c.Id, c.Semester })
.GroupBy(c => c.Semester)
.ToDictionary(cs => cs.Key, cs => cs.ToList());
翻译成:
SELECT [c].[Semester], [c].[Id]
FROM [Courses] AS [c]
ORDER BY [c].[Semester]
如果结果是:
学期 | ID |
---|---|
2023年S1 | 1个 |
2023年S1 | 4个 |
2023 中二 | 2个 |
... | ... |
然后 EF Core 读起来像:
您了解排序的好处。
关于报错,不知道EF Core不行。 查询声音合法。 也许这不应该在这个时候实施。
关于您尝试的内容,将排序的分组枚举转换为字典。 这很奇怪,因为字典不可排序。 然后这对元素进行排序并将它们放在松散的位置。
如果Dictionary
看起来是有序的,那是巧合,而不是功能。 在实习生中,字典按键排序元素有代码,一般是排序顺序......但不是每次都这样。
如果你想要一个排序的字典,你可以使用SortedDictyonary 。 但如果您需要自定义排序规则,这可能会很棘手,例如:
context
.Courses
.Select(c => new { c.Id, c.Semester })
.GroupBy(c => c.Semester)
.ToImmutableSortedDictionary(cs => cs.Key, cs => cs.ToList(), new ReverseComparer<string>());
public class ReverseComparer<T> : IComparer<T>
{
private IComparer<T> _comparer = Comparer<T>.Default;
public int Compare(T? x, T? y)
{
return _comparer.Compare(x, y) * -1;
}
}
您遇到的异常很可能是由于实体框架无法将OrderBy
子句转换为 SQL。 从数据库中检索数据后, OrderBy
子句在 memory 中执行,这就是当您删除它并仅保留GroupBy
子句时它起作用的原因。
但是,如果您想按降序对字典进行排序,只需对ToDictionary
结果调用Reverse
方法即可:
var professorGradedCourses = _dbContext.Courses
.Where(course => course.ProfessorId == professorId && course.Grades.Any())
.Select(course => new CourseViewModel
{
Title = course.Title,
Semester = course.Semester,
})
.GroupBy(course => course.Semester)
.OrderByDescending(course => course.Key)
.ToDictionary(group => group.Key,
group => group.ToList())
.Reverse();
这样,字典将根据学期降序排列。
试一试,让我知道它是如何为你工作的。
编辑:
将 IEnumerable 转换回字典应该像这样工作:
var professorGradedCourses = _dbContext.Courses
.Where(course => course.ProfessorId == professorId && course.Grades.Any())
.Select(course => new CourseViewModel
{
Title = course.Title,
Semester = course.Semester,
})
.GroupBy(course => course.Semester)
.OrderByDescending(course => course.Key)
.ToDictionary(group => group.Key,
group => group.ToList())
.Reverse()
.ToDictionary(pair => pair.Key,
pair => pair.Value);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.