简体   繁体   中英

LINQ, Skip and Take ordering

I was doing some testing with Take and Skip and I found that:

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() returns an IQueryable generated by NHibernate (3.3.3GA, using a SQL Server 2008 R2 DB).
skipTake and takeSkip contain the same exact sequence of 10 objects.
But if I write

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 contains the same sequence as the above example while takeSkip contains a different sequence of just 5 objects.

Do Take and Skip calls get reordered when they are applied on a IQueryable?
I would love to get some insight on this.

Thanks in advance.

It looks like this is due to a bug in particular versions of nhibernate:

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

BEWARE: In versions prior to 3.3.3.CR1, the handling of the LINQ Take() method was flawed - no matter where in the query Take() was placed it was always applied as if it had been placed at the end. 3.3.3 fixes this, so that Take() now correctly follows the .Net semantics. That is, in 3.3.3, the following queries might now give different results:

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

Starting with 3.3.3, the first query will generate a subquery to correctly apply the row limit before the where-clause.

Do Take and Skip calls get reordered when they are applied on a IQueryable? I would love to get some insight on this.

I think the answer to this question should be formulated this way:

skipTake is the result of skipping the first 5 elements of the IQueriable and taking the next 10. So for example in a list of ordered numbers from 1 to 20 skipTake would be the sublist 6 --> 15.

takeSkip is the result of taking the first 10 elements of the IQueriable and then skipping the first 5 elements of the sublist (!). So using the list from the previous example, takeSkip would be the sublist 6 --> 10.

For the first part of your observation (where the skipTake and takeSkip contain the same 10 elements), this should be considered as wrong behaviour (or probably even a bug) in the NHibernate implementation.

Firstly I find that @ https://stackoverflow.com/users/2617732/rdans answer is irrelevant to your specific example since you do not apply a where clause after the Take .

That said, in example A both skipTake and takeSkip should generate the same SQL where it it would select the first 10 rows after the 5th row, (for SQL server for example that would be with ROW_NUMBER OVER(...) ).

It seems to me that in example B you are trying to perform the Take and Skip not on an IQuerable but on a System.Collections.Generic.List<> (which naturally is not NHibernate territory).

In this case objects has N elements.

skipTake will first Skip 10 elements and from the resulting enumeration (which enumerates over N-10 elements) then Take the first 5 elements.

Given then the results are already ordered the result of skipTake s ToList should be the same with the example A.

takeSkip on the other hand first Take s 10 elements and from the resulting enumeration (which enumerates over 10 elements) Skip s the first 5.

So, that's that

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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