【问题标题】:OrderBy().FirstOrDefault(<condition>) vs. Where(<condition>).OrderBy().FirstOrDefault()OrderBy().FirstOrDefault(<condition>) 与 Where(<condition>).OrderBy().FirstOrDefault()
【发布时间】:2015-07-01 13:34:44
【问题描述】:

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

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% 相信。

【问题讨论】:

  • 也许您应该对已发布的答案做出反应。我想他们已经解释了这个问题。

标签: sql-server linq-to-sql entity-framework-6


【解决方案1】:

即使related bug report 已在此处回答 SO 已关闭,但这显然是一个错误。我在GroupBy 中使用OrderByDescending 后跟FirstOrDefault 和谓词的类似构造遇到了同样的问题。

似乎实体框架不支持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) 语句。但这样做会导致您的查询出错!

【讨论】:

    【解决方案2】:

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

    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 之后。

    【讨论】:

      【解决方案3】:

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

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

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

      暂时你别无选择,只能选择有效的模式。

      【讨论】:

        【解决方案4】:

        要解析调用 ToList() 方法

        【讨论】:

          【解决方案5】:

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

          【讨论】:

          • 所有这些查询都保留在 IQueryable 域中。 IEnumerable 在这里不是问题。
          • 好点我没有去阅读只是在我的头顶上说出来。 IEnumerable msdn.microsoft.com/en-us/library/vstudio/… 确实有 firstordefault 的实现,但我的答案在这方面缺乏。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-01-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多