【问题标题】:uses First/FirstOrDefault/Last/LastOrDefault operation without OrderBy and filter which may lead to unpredictable results使用没有 OrderBy 和过滤器的 First/FirstOrDefault/Last/LastOrDefault 操作,这可能会导致不可预测的结果
【发布时间】:2020-11-06 14:08:07
【问题描述】:

我有一个 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

更新:

我通过 cmets 更改了代码。

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, .....) 可以 不翻译,将在本地评估。

【问题讨论】:

  • 尚不清楚您的警告来自何处:它不是内置警告之一。你能分享一下警告的代码吗?通常它的格式为“XX1234”
  • 试试:.Select(x => x.OrderBy(r=> r.Key).First()).
  • 为什么取消 OrderBy 会产生不可预知的结果?不需要 OrderBy,因此您可以删除。如果没有 OrderBy,结果会更加随机,因为代码会删除第一个项目并取决于写入数据的顺序。假设你每个月把所有的账单都存起来,然后堆成一堆。然后在月底,您只需选择前 5 名账单并支付。剩下的留到下个月。那么如果你不付房租怎么办?你会被驱逐吗?
  • 警告是有道理的,如果你没有定义顺序,那么“第一”是什么意思?这将是一些数据库定义的顺序中的第一个,甚至不能保证每次都相同。
  • @Hello 无论有没有警告,您的查询都会在本地进行评估。

标签: c# linq entity-framework-core


【解决方案1】:

我们有这样的表达方式

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

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

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

要解决此问题,您必须先致电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

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

【讨论】:

  • 我根据您的建议更新了问题,但收到了另一个警告。
  • 我刚刚删除了GroupByOrderByFirst() 等,然后警告就消失了。
  • @你好,确定它已经消失了。但现在您不再获得相同的数据了。
  • 一个后续问题。对于纯 C# 使用 First() 返回内存中的序列(与 sql 无关),如果我没有 OrderBy(),可以吗?谢谢。
  • 它应该可以工作,但就像 SQL 一样,没有定义哪个项目是 First()。因此发出警告。
猜你喜欢
  • 2021-07-24
  • 1970-01-01
  • 2023-03-13
  • 1970-01-01
  • 1970-01-01
  • 2022-07-22
  • 2021-12-07
  • 1970-01-01
  • 2017-05-04
相关资源
最近更新 更多