EF Core 7.0 (PostgresSQL) 生成 IQueryable with RowNumber concept

EF Core 7.0 (PostgresSQL) build IQueryable with RowNumber concept

提问人:sarvpk 提问时间:11/2/2023 最后编辑:sarvpk 更新时间:11/3/2023 访问量:140

问:

我有一个场景,一个学生(由 StudentId 标识)拥有许多课程。每门课程都有 StudentId(FK) 和成绩(简单文本类型)。我想构建一个 IQueryable,在按降序排序时按课程一年级提取 10 个 StudentId(跳过前 5 个)。

尝试了以下查询,但得到System.InvalidOperationException:无法转换LINQ表达式

    var studentQuery= context.Set<Student>().AsNoTracking().Where(x=>x.IsActive);
    var courseQuery= context.Set<Course>().AsNoTracking()
        .OrderByDescending(x => x.Grade)
        .GroupBy(x => x.StudentId)
        .SelectMany(x => x.Select((b, i) => new { b, rn = i + 1 }))
        .Where(x => x.rn == 1).Select(x => x.b);
    return studentQuery.Join
    (
        courseQuery,
        x => x.StudentId,
        y => y.StudentId,
        (a, b) => b
    )
    .Skip(5)
    .Take(10)
    .Select(x => x.StudentId);
C# PostgreSQL LINQ 到实体 EF-Core-7.0

评论


答:

0赞 sarvpk 11/3/2023 #1

我能够使用下面的查询来解决问题。使用 GroupJoin(而不是 Join)来处理“没有任何课程的学生”用例。当课程没有成绩时,请使用 null 合并运算符来修复此问题。这些学生将在分页中排在最后。我很惊讶 Max 在文本/字符串数据类型上工作。虽然这正在起作用,但我相信有更好的方法可以解决这个问题。

var studentQuery= context.Set<Student>().AsNoTracking().Where(x=>x.IsActive);   
var courseQuery= context.Set<Course>().AsNoTracking();

return studentQuery.GroupJoin
(
    courseQuery,
    x => x.StudentId,
    y => y.StudentId,
    (a, courses) => new { a.StudentId, courses }
)
.SelectMany(x => x.courses.DefaultIfEmpty(), (a, b) => new { a, b })
.GroupBy(x => x.a.StudentId)
.OrderByDescending(x => x.Max(y => y.b.Grade ?? string.Empty))
.Skip(5)
.Take(10)
.Select(x => x.Key);

生成的 postgres SQL 如下所示

SELECT r.student_id
FROM student AS r
LEFT JOIN (
    SELECT r0.grade, r0.student_id
    FROM course AS r0
) AS t ON r.student_id = t.student_id
WHERE r.is_active
GROUP BY r.student_id
ORDER BY max(COALESCE(t.grade,'')) DESC
LIMIT 10 OFFSET 5