简体   繁体   English

实体框架:跳过/接受功能

[英]Entity Framework: Skip/Take functionality

I'm just curious how Skip and Take functions work in Entity Framework (using EF 6.1).我只是好奇 Skip 和 Take 函数在 Entity Framework(使用 EF 6.1)中是如何工作的。

If I do:如果我做:

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

I get some list (noticed that one event is completely gone).我得到了一些列表(注意到一个事件完全消失了)。

If I do:如果我做:

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

I get another list and that gone event in previous query is present here.我得到另一个列表,上一个查询中的消失事件出现在这里。

Anyone has idea why do I have to Skip() ZERO entities in order to Take() what I'm supposed to take?任何人都知道为什么我必须Skip()零个实体才能Take()我应该采取的措施? It makes almost no sense (at least for me)...这几乎没有任何意义(至少对我而言)......

PS I can't use SQL Server Profiler to check what queries are generated. PS 我无法使用 SQL Server Profiler 来检查生成了哪些查询。

The accepted answer is mostly correct in describing how emitted queries different though the details can vary.尽管细节可能有所不同,但已接受的答案在描述发出的查询如何不同方面大多是正确的。 I have commonly seen the entity framework emit the query as "TOP (@maxPageSize) .... WHERE [ROW_NUMBER] > @skip" which is probably the same difference, but I am not 100% certain if it generates the same query execution plan.我经常看到实体框架将查询发出为“TOP (@maxPageSize) .... WHERE [ROW_NUMBER] > @skip”,这可能是相同的区别,但我不能 100% 确定它是否生成相同的查询执行计划。

The important difference to clearly note is that this can produce differing results when your "ORDER BY" does not contain a unique column name.需要明确指出的重要区别是,当您的“ORDER BY”不包含唯一的列名时,这可能会产生不同的结果。

In code that I had written, we omitted the "Skip" when the @skip value was 0. We had one entry appear as the final entry.在我编写的代码中,当@skip 值为 0 时,我们省略了“Skip”。我们将一个条目作为最后一个条目出现。 If you then went to the next page where the @skip value was applied, the same entry appeared as the first item on that page.如果您随后转到应用了@skip 值的下一页,则相同的条目将显示为该页面上的第一项。 The "tied" item that should have been in at least one of those positions never appeared.应该至少出现在这些位置之一的“绑定”项目从未出现过。 Here is the SQL that each emitted:这是每个发出的 SQL:

No skip (generated on page 1):不跳过(在第 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

Skip:跳过:

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

The important take away is to go all the way back to "SQL 101" and remember that without "order by", order is not guaranteed .重要的一点是回到“SQL 101”并记住, 如果没有“order by”,则无法保证顺序 This difference in emitting causing an item to be duplicated on multiple pages of a grid while another item never shows up at all has made me see that this is even more paramount in Entity Framework where the exact SQL produced is not as directly in your control and can differ in future EF versions unexpectedly .发射的这种差异导致一个项目在网格的多个页面上重复,而另一个项目根本不显示,这让我看到这在实体框架中更为重要,其中生成的确切 SQL 并不直接在您的控制中,并且在未来的 EF 版本中可能会出乎意料地不同

You can currently avoid this by always including Skip(0) which will emit the second query but with {skip number} simply being zero.您目前可以通过始终包含 Skip(0) 来避免这种情况,它会发出第二个查询,但 {skip number} 只是为零。 This appears to, in my tests, always emit the same ordering for tiebreakers using the default rules of T-SQL.在我的测试中,这似乎总是使用 T-SQL 的默认规则为决胜局发出相同的顺序。 I believe it is not best practice to assume that this will necessarily work in future versions of Entity Framework as such though.我相信假设这在实体框架的未来版本中一定会起作用并不是最佳实践。 I would suggest that instead, you consider exploring a tie-breaking strategy of your own.相反,我建议您考虑探索自己的打破平局的策略 In my case, it should be possible to break the tie on "Table1ID" as that represents a unique identity primary key column.在我的情况下,应该可以打破“Table1ID”的关系,因为它代表了一个唯一的身份主键列。 The attached article gives suggestions for more complicated situations though.不过,随附的文章为更复杂的情况提供了建议。

There is completely different queries generated.生成了完全不同的查询。 In first case you simply take top N rows:在第一种情况下,您只需取前 N 行:

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

In second case row number is used to filter ordered rows:在第二种情况下,行号用于过滤有序行:

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() returns row numbers starting from 1. If page size is 5 then first query will return you top 5 rows, as expected. ROW_NUMBER()返回从 1 开始的行号。如果页面大小为 5,那么第一个查询将按预期返回前 5 行。 Second query will return you rows with numbers 1, 2, 3, 4, 5. So, there should not be any difference between these two queries when @skip parameter equals to zero.第二个查询将返回带有数字 1、2、3、4、5 的行。因此,当 @skip 参数等于 0 时,这两个查询之间应该没有任何区别。

Make sure you are really executing exactly specified queries with exactly same parameters.确保您确实使用完全相同的参数执行完全指定的查询。 Make sure Events table have exactly same data when you run these queries.运行这些查询时,请确保事件表具有完全相同的数据。 Check if you are using latest versions of assemblies.检查您是否使用最新版本的程序集。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM