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

时间:2015-07-01 13:34:44

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

我在C#项目中使用EntitiyFramework 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%相信。

5 个答案:

答案 0 :(得分:2)

即使related bug report已关闭,因此在SO上回答,这显然是一个错误。我使用类似的OrderByDescending结构,然后FirstOrDefault带有谓词,在GroupBy内,遇到了同样的问题。

似乎实体框架不支持带有谓词的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())

但是,在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)语句。但这样做会使你的查询出错!

答案 1 :(得分:1)

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

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

我认为这应该有用,或者至少非常适合它有效。创建一个指向此问题的简短Github问题。团队非常敏感。

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

答案 2 :(得分:1)

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

要解决,请调用ToList()方法

答案 4 :(得分:-1)

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