首页 > 解决方案 > 使用没有 OrderBy 和过滤器的 First/FirstOrDefault/Last/LastOrDefault 操作,这可能会导致不可预测的结果

问题描述

我有一个 linq 查询,它给了我警告,但它仍然有效。我想摆脱警告。

uses First/FirstOrDefault/Last/LastOrDefault operation without OrderBy and filter which may lead to unpredictable results.

linq 查询是

var list = (from u in _db.user
        join r in _db.resource on u.userId equals r.userId
        join t in _db.team on u.bossId equals t.bossId
        where r.pid == pid
        select new MyDto
        {
            pid = pid,
            userId = u.userId,
            teamId = t.teamId,
            name = t.name
        }).GroupBy(d => d.userId).Select(x => x.First()).OrderBy(y => y.userId).ToList();

我使用 EntityFramework Core 2.1

更新:

我通过评论更改了代码。

var list = (from u in _db.user
        join r in _db.resource on u.userId equals r.userId
        join t in _db.team on u.bossId equals t.bossId
        where r.pid == pid
        select new MyDto
        {
            pid = pid,
            userId = u.userId,
            teamId = t.teamId,
            name = t.name
        })
        .GroupBy(d => d.userId)
        .Select(x => x.OrderBy(y => y.userId)
        .First())
        .ToList();

然后有一个不同的警告。

无法翻译 LINQ 表达式 'GroupBy([user].userId, new MyDto() {pid = Convert(_8_locals1_pid_2, Int16), userId = [user].UserId, .....) 并将在本地进行评估。

标签: c#linqentity-framework-core

解决方案


我们有这个表达

.Select(x => x.First())

哪个记录将是该表达式的第一个?没有办法知道,因为此时后面的OrderBy()子句还没有处理。每次对相同数据运行相同查询时,您可能会得到不同的结果,具体取决于从数据库返回记录的顺序。结果是不可预测的,正如错误消息所说的那样。

但是数据库肯定会每次都以相同的顺序返回它们吗? ,你不能这样假设。除非查询中有 ORDER BY 子句,否则不会定义SQL 查询中的结果顺序。大多数时候你会得到主键排序(它不必匹配插入顺序!),但是有很多事情可以改变这一点:匹配不同的索引,加入到具有不同顺序或不同索引的表,与同一张表上的另一个查询并行执行 + 循环索引遍历,等等。

要解决此问题,您必须先调用OrderBy() ,然后才能调用First()

再深入一点,这甚至不是 SQL 的一部分。这项工作正在您的客户端上进行。这不好,因为表上的任何索引都不再可用。应该可以在数据库服务器上完成所有这些工作,但选择组的第一条记录可能意味着您需要横向连接/APPLY 或 row_number() 窗口函数,这些函数很难用 EF 重现。要完全删除所有警告,您可能必须编写原始 SQL 语句:

select userId, teamId, name, pid
from (
    select u.userId, t.teamId, t.name, r.pid, row_number() over (order by u.userId) rn
    from User u
    inner join resource r on r.userId = u.userId
    inner join team t on t.bossId = u.bossId
    where r.pid = @pid
) d
where d.rn = 1

环顾四周,可以在 EF 中使用 row_number(),但在这一点上,我个人发现 SQL 更易于使用。我的观点是 ORM 对这些更复杂的查询没有帮助,因为你仍然必须知道你想要的 SQL,并且你还必须知道 ORM 的复杂性才能构建它。换句话说,本应让你的工作更轻松的工具反而变得更难了。


推荐阅读