OrderBy().FirstOrDefault(<condition>) 与 Where(<condition>).OrderBy().FirstOrDefault()

OrderBy().FirstOrDefault(<condition>) vs. Where(<condition>).OrderBy().FirstOrDefault()

我在 C# 项目中使用 EntitiyFramework 6.1.3 和 SQL 服务器。 我有两个基本上应该做同样的查询。

1.

Exams.GroupBy(x=>x.SubjectID).Select(x => x.OrderBy(y => y.Level.NumericValue).FirstOrDefault(y => y.Date.Value > DateTime.Today))

2.

Exams.GroupBy(x=>x.SubjectID).Select(x => x.Where(y => y.Date.Value > DateTime.Today).OrderBy(y => y.Level.NumericValue).FirstOrDefault())

但是,第一个查询的结果与我将离开订单的结果相同。 第二个按预期工作。

当我查看通过 Linqpad 生成的 SQL 时,第一个查询中没有 order by 子句。

我的问题是,为什么第一个查询不像第二个查询那样工作? 我一直以为

.Where(<condition>).FirstOrDefault() == .FirstOrDefault(<condition>)

喜欢这里已经回答:.Where(<condition>).FirstOrDefault() vs .FirstOrDefault(<condition>)

编辑: 我又玩了一会儿,发现这两个查询产生相同的 SQL 输出。

Exams.GroupBy(x => x.SubjectID).Select(x => x.FirstOrDefault(y => y.Date.Value > DateTime.Today))
Exams.GroupBy(x => x.SubjectID).Select(x => x.OrderBy(y => y.Level.NumericValue).FirstOrDefault(y => y.Date.Value > DateTime.Today))

尽管它看起来像一个错误,但我仍然不是 100% 相信。

.Where(<condition>).FirstOrDefault() 将从您的条件中 return 一个 IEnumerable 列表,然后它从该列表中获取第一个或默认值。其中第二个 .FirstOrDefault(<condition>) 将应用条件并获得与您的条件匹配的第一个值。所以两者会执行不同

这个问题的答案取决于 LINQ 提供程序,这里是 EF。 LINQ 提供程序支持的特定模式很少完整记录。我不知道关于此问题的任何 EF 文档。目前尚不清楚这是一个错误还是它是否应该起作用。

LINQ 技术本身无法保证它始终有效。

我认为这应该有效,或者至少非常希望它有效。创建一个指向此问题的简短 Github 问题。团队反应迅速。

目前您别无选择,只能选择有效的模式。

不确定这是否是问题的根本原因,但这两个查询之间存在细微差别 - OrderBy 和 Where 的相对位置。在关系世界中,过滤器使子查询中完成的排序无效。 EF 尝试通过过滤器后的 "lifiting" 一些 orderby 来补偿这种差异,并且可能无法正确提升这种模式。尝试在第二个查询中切换 OrderBy 和 Where around:

Exams.GroupBy(x => x.SubjectID).Select(x => x.OrderBy(y => y.Level.NumericValue).Where(y => y.Date.Value > DateTime.Today).FirstOrDefault())

并查看您是否仍然获得与未更改版本相同的生成 SQL。如果这样做,那肯定是 EF 中的错误(我希望生成的 sql 与查询 1 相同,但与原始查询 2 不同)。否则可能只是提升算法对阶数的限制。

一般来说,如果您想让 EF 更容易 - 最好将 OrderBy-s 放在 Where-s 之后。

尽管 related bug report 已关闭,因为在 SO 上已回答,但这显然是一个错误。我 运行 在 GroupBy.

中使用 OrderByDescending 后跟带谓词的 FirstOrDefault 的类似结构来解决这个完全相同的问题

看来 Entity Framework 不支持带有谓词 的 FirstOrDefault 语句 ,至少在这个问题的上下文中不支持。正如问题所述,以下表达式产生了一个带有 ORDER BY 子句的正确 SQL 查询:

Exams
    .GroupBy(x => x.SubjectID)
    .Select(x => x
        .Where(y => y.Date.Value > DateTime.Today)
        .OrderBy(y => y.Level.NumericValue)
        .FirstOrDefault())

但是,将 any 谓词添加到 FirstOrDefault 语句会混淆 EF 并导致 查询没有 ORDER BY:

Exams
    .GroupBy(x => x.SubjectID)
    .Select(x => x
        .Where(y => y.Date.Value > DateTime.Today)
        .OrderBy(y => y.Level.NumericValue)
        .FirstOrDefault(y => true)) // Tautology shouldn't have any effect, but it does!

解决此问题的唯一方法是将 FirstOrDefault(predicate) 语句拆分为 Where(predicate).FirstOrDefault()。只需确保在您的代码中添加注释来解释此决定,因为 ReSharper 正确地建议使用单个 FirstOrDefault(predicate) 语句。但是这样做会使您的查询出错!

要解决调用 ToList() 方法