【问题标题】:LINQ, Skip and Take orderingLINQ、跳过和接受订购
【发布时间】:2015-05-20 23:18:55
【问题描述】:

我正在使用 Take and Skip 进行一些测试,我发现:

var objects = (from c in GetObjects() orderby c.Name select c);
var skipTake = objects.Skip(5).Take(10).ToList();
var takeSkip = objects.Take(10).Skip(5).ToList();

GetObjects() 返回一个由 NHibernate(3.3.3GA,使用 SQL Server 2008 R2 DB)生成的 IQueryable。
skipTake 和 takeSkip 包含完全相同的 10 个对象序列。
但是如果我写

var objects = (from c in GetObjects() orderby c.Name select c).ToList();
var skipTake = objects.Skip(5).Take(10).ToList();
var takeSkip = objects.Take(10).Skip(5).ToList();

skipTake 包含与上述示例相同的序列,而 takeSkip 包含不同的序列,仅包含 5 个对象。

Take 和 Skip 调用在应用于 IQueryable 时是否会重新排序?
我很想对此有所了解。

提前致谢。

【问题讨论】:

  • 对比度从何而来?
  • 第二个听起来像是正确的行为。你的IQueryable 到底来自哪里?这几乎听起来像是任何东西都会在数据源上生成所需的查询并以某种方式弄乱SkipTake
  • @Ralf,是的,我在“清理后的代码”中留下了一个错字,现在已更正。 James Thorpe,“IQueryable”来自 NHibernate 查询,难道 NHibernate 正在重新排序 SQL 生成中的调用?
  • 将此称为“预期行为”有点牵强。如果提供者不能做正确的事情,它应该失败,而不是做不正确的事情。
  • @Rawling 哦,我同意,我会说这绝对看起来像是他们的一个错误 - 只是说他们可能会争辩说它只是这样设计并记录在案。我不知道这方面的任何一种方式,这不是我使用过的工具。

标签: c# linq nhibernate


【解决方案1】:

看起来这是由于特定版本的 nhibernate 中的错误:

http://sourceforge.net/p/nhibernate/news/2013/03/nhiberate-333ga-released/

注意:在 3.3.3.CR1 之前的版本中,LINQ Take() 方法的处理 有缺陷——无论 Take() 放在查询的哪个位置,它都是 总是像放在最后一样应用。 3.3.3 修复了这个问题, 以便 Take() 现在正确地遵循 .Net 语义。也就是说,在 从 3.3.3 开始,以下查询现在可能会给出不同的结果:

session.Query<Foo>.OrderBy(...).Take(5).Where(...);
session.Query<Foo>.Where(...).OrderBy(...).Take(5);

从 3.3.3 开始,第一个查询会生成一个子查询来正确 在 where 子句之前应用行限制。

【讨论】:

  • 这似乎是我看到的问题,但我使用的是 3.3.3GA,所以应该修复该错误。也许他们用 Take + Where 修复了订单,但 Skip + Take 的重新排序仍然存在?
  • 是的,它看起来确实可能是另一种情况,特别是考虑到您使用的版本应该可以正常工作。无论如何,我都倾向于尝试最新版本。如果他们知道他们提到的问题并修复了它,那么他们可能也知道您的问题并在以后的版本中修复了它。如果不是,那么他们使用的措辞表明他们会同意这是不正确的行为,因此如果它没有在最新版本中修复,接下来会将其作为错误报告给他们。
  • 您可能会在他们的问题跟踪器中找到一些相关信息:nhibernate.jira.com/browse/…
  • 之前尝试过简单的搜索,但没有找到任何具体的内容。稍后我会再试一次(也许会使用 NHibernate Profiler 查看生成的 sql)。
【解决方案2】:

Do Take 和 Skip 调用在应用于 可查询?我很想对此有所了解。

我认为这个问题的答案应该这样表述:

skipTake 是跳过 IQueriable 的前 5 个元素并取接下来的 10 个元素的结果。例如,在从 1 到 20 的有序数字列表中,skipTake 将是子列表 6 -- > 15.

takeSkip 是获取 IQueriable 的前 10 个元素然后跳过子列表 (!) 的前 5 个元素的结果。因此,使用上一个示例中的列表,takeSkip 将是子列表 6 --> 10。

对于您观察的第一部分(skipTake 和 takeSkip 包含相同的 10 个元素),这应该被视为 NHibernate 实现中的错误行为(甚至可能是错误)。

【讨论】:

    【解决方案3】:

    首先我发现@https://stackoverflow.com/users/2617732/rdans 答案与您的具体示例无关,因为您没有在Take 之后应用where 子句。

    也就是说,在示例 A 中,skipTaketakeSkip 都应该生成相同的 SQL,它将选择第 5 行之后的前 10 行(例如,对于 SQL 服务器,它将使用 ROW_NUMBER OVER(...)) .

    在我看来,在示例 B 中,您尝试执行 TakeSkip 不是在 IQuerable 上,而是在 System.Collections.Generic.List&lt;&gt; 上(这自然不是 NHibernate 领域)。

    在这种情况下,objects 有 N 个元素。

    skipTake 将首先 Skip 10 个元素,然后从生成的枚举(枚举 N-10 个元素)然后 Take 前 5 个元素。

    假设结果已经排序,skipTakes ToList 的结果应该与示例 A 相同。

    另一方面,

    takeSkip 首先是 Takes 10 个元素,然后从结果枚举(枚举超过 10 个元素)Skips 前 5 个元素。

    原来如此

    【讨论】:

      猜你喜欢
      • 2013-01-06
      • 2012-01-02
      • 1970-01-01
      • 2022-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多