【问题标题】:Entity Framework: Skip/Take functionality实体框架:跳过/获取功能
【发布时间】:2014-05-23 06:57:09
【问题描述】:

我只是好奇 Skip 和 Take 函数在 Entity Framework 中是如何工作的(使用 EF 6.1)。

如果我这样做:

db.Events.OrderByDescending(x => x.Date).Take(maxPageSize).ToList();

我得到了一些列表(注意到一个事件完全消失了)。

如果我这样做:

db.Events.OrderByDescending(x => x.Date).Skip(0).Take(maxPageSize).ToList();

我得到另一个列表,之前查询中的消失事件出现在这里。

任何人都知道为什么我必须Skip() ZERO 实体才能Take() 我应该采取什么?这几乎没有意义(至少对我来说)......

附:我无法使用 SQL Server Profiler 检查生成了哪些查询。

【问题讨论】:

  • 没有一个有效的例子,很难看出这种说法的可信度。Skip(0) 不应该做任何事情
  • 提示:将函数附加到DbContext.Database.Log (Action<string>) 以查看生成的SQL。
  • @Sayse,我不是真正的巨魔或骗子。如果我没有亲眼看到,我不会问这个问题。我也认为Skip(0) 应该什么都不做。 + 我看到了您认为重复的问题,但这根本不能回答我的问题。
  • @YOhan - 我没有说你是我只是说这听起来很难相信,这就是我去搜索信息的原因.. 相关专栏右侧还有其他链接可能感兴趣。我提出的重复的那个似乎和你有完全相同的问题

标签: c# entity-framework entity-framework-6


【解决方案1】:

尽管细节可能会有所不同,但在描述发出的查询有何不同方面,公认的答案大多是正确的。我经常看到实体框架将查询发出为“TOP (@maxPageSize) .... WHERE [ROW_NUMBER] > @skip”,这可能是相同的区别,但我不能 100% 确定它是否生成相同的查询执行计划。

需要明确注意的重要区别是,当您的“ORDER BY”不包含唯一的列名时,这会产生不同的结果。

在我编写的代码中,当@skip 值为0 时,我们省略了“Skip”。我们有一个条目作为最终条目出现。如果您随后转到应用了@skip 值的下一页,则相同的条目将作为该页面上的第一项出现。应该至少处于其中一个位置的“绑定”项目从未出现。这是每个发出的 SQL:

没有跳过(在第 1 页生成):

SELECT TOP ({take number}) [Extent1].[Table1ID] AS [Table1ID], {snip a lot of other columns}
        FROM  [dbo].[Table1] AS [Extent1]
        LEFT OUTER JOIN [dbo].[Table2] AS [Extent2] ON [Extent1].[Table2ID] = [Extent2].[Table2ID]
        WHERE ({my where clause conditions})
        ORDER BY [Extent2].[MySortColumn] ASC

跳过:

SELECT TOP ({take number}) [Filter1].[Table1ID], {snip a lot of other columns}
        FROM ( SELECT [Extent1].[Table1ID] AS [Table1ID], {snip a lot of other columns}, row_number() OVER (ORDER BY [Extent2].[MySortColumn] ASC) AS [row_number]
            FROM  [dbo].[Table1] AS [Extent1]
            LEFT OUTER JOIN [dbo].[Table2] AS [Extent2] ON [Extent1].[Table2ID] = [Extent2].[Table2ID]
            WHERE ({my where clause conditions})
        )  AS [Filter1]
        WHERE [Filter1].[row_number] > {skip number}
        ORDER BY [Filter1].[MySortColumn] ASC

重要的是回到“SQL 101”并记住without "order by", order is not guaranteed。这种发射差异导致一个项目在网格的多个页面上重复,而另一个项目根本不显示,这让我看到这在实体框架中更为重要,因为生成的确切 SQL 并不直接在您的控制中,并且在未来的 EF 版本中可能会出乎意料地不同

您目前可以通过始终包含将发出第二个查询但 {skip number} 简单为零的 Skip(0) 来避免这种情况。在我的测试中,这似乎总是使用 T-SQL 的默认规则为决胜局发出相同的顺序。我认为假设这在实体框架的未来版本中必然会起作用并不是最佳实践。相反,我建议您考虑探索自己的tie-breaking strategy。在我的情况下,应该可以打破“Table1ID”的关系,因为它代表一个唯一的身份主键列。不过,随附的文章针对更复杂的情况给出了建议。

【讨论】:

  • 更改了接受的答案,因为这实际上解释了问题。谢谢!
【解决方案2】:

生成了完全不同的查询。在第一种情况下,您只需获取前 N 行:

SELECT TOP(@maxPageSize) ...
FROM [Events]
ORDER BY [Date] DESC

在第二种情况下,行号用于过滤有序行:

SELECT ...
FROM (SELECT ROW_NUMBER() OVER (ORDER BY [Date] DESC) AS [ROW_NUMBER], ...
      FROM [Events])
WHERE [ROW_NUMBER] BETWEEN @skip + 1 AND @skip + @maxPageSize
ORDER BY [ROW_NUMBER]

ROW_NUMBER() 返回从 1 开始的行号。如果页面大小为 5,则第一个查询将返回前 5 行,如预期的那样。第二个查询将返回编号为 1、2、3、4、5 的行。因此,当 @skip 参数等于 0 时,这两个查询之间应该没有任何区别。

确保您确实使用完全相同的参数执行完全指定的查询。运行这些查询时,请确保事件表具有完全相同的数据。检查您是否使用最新版本的程序集。

【讨论】:

  • Erm... 查询已经按Take() 之前的日期排序,这就是我感到困惑的原因。假设查询应该返回有序查询的最佳结果,我错了吗? WHERE 条件(以及Skip(0) 无论如何)在我的情况下看起来完全无关紧要。检查了调试器中的所有记录 - 都是一样的.. 难倒。无论如何,感谢您的详细回复,我将此标记为答案,但仍然想知道我们这边或 EF 本身是否有问题..
  • @YOhan 我认为 EF 对于这样简单的查询非常好。我添加了一些可能导致结果不同的原因。我建议您尝试依次执行这两个查询,并在中间没有任何内容的情况下验证结果。当您找到问题原因时,请更新问题 - 这可能对其他人有帮助)
猜你喜欢
  • 2011-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多