tags:

views:

333

answers:

4

How would you implement paging in a LINQ query? Actually for the time being, I would be satisfied if the sql TOP function could be imitated. However, I am sure that the need for full paging support comes up sooner later anyway.

var queryResult = from o in objects
                  where ...
                  select new
                      {
                         A = o.a,
                         B = o.b
                      }
                   ????????? TOP 10????????
+6  A: 

You're looking for the Skip and Take extension methods. Skip moves past the first N elements in the result, returning the remainder; Take returns the first N elements in the result, dropping any remaining elements.

See MSDN for more information on how to use these methods: http://msdn.microsoft.com/en-us/library/bb386988.aspx

For example:

int numberOfObjectsPerPage = 10;
var queryResultPage = queryResult
  .Skip(numberOfObjectsPerPage * pageNumber)
  .Take(numberOfObjectsPerPage);
David Pfeffer
Should I use the same technique over SQL with a huge database, will it take the entire table into memory first and then throw away the the unwanted?
No. LINQ expressions are not actually executed (assuming the object you are querying over is IQueryable and not IEnumerable. A database should be IQueryable for any sane implementation). Instead, they are turned into expression trees by the compiler, and then the LINQ driver for your database converts it into SQL. So, for example, the `Take` is probably turned into a `TOP` SQL expression.
David Pfeffer
If you're interested in what's going on under the hood, by the way, most LINQ database drivers provide a way to get debug output information for the actual SQL that is being executed.
David Pfeffer
Rob Conery blogged about a PagedList<T> class that may help you get started. http://blog.wekeroad.com/blog/aspnet-mvc-pagedlistt/
jrotello
+2  A: 

EDIT - Removed Skip(0) as it's not necessary

var queryResult = from o in objects
                      where ...
                      select new
                          {
                             A = o.a,
                             B = o.b
                          }.Take(10);
Jack Marchetti
Shouldn't you change the order of the Take/Skip methods? Skip(0) after Take does not make sense. Thanx for giving your the example in query style.
No, he's right. `Take` 10, `Skip` 0 takes the first 10 elements. `Skip` 0 is pointless and shouldn't ever be done. And the order of `Take` and `Skip` matters -- `Skip` 10, `Take` 10 takes elements 10-20; `Take` 10, `Skip` 10 returns no elements.
David Pfeffer
@David, my bad you're right.
Jack Marchetti
You might also need brackets around the query before calling Take. (from ... select ...).Take(10). I called the construct with selecting a string. Without brackets, the Take returned the first 10 chars of the string instead of limiting the query result :)
A: 
   ( for o in objects
    where ...
    select new
   {
     A=o.a,
     B=o.b
   })
.Skip((page-1)*pageSize)
.Take(pageSize)
Noel
+2  A: 

Using Skip and Take is definitely the way to go. If I were implementing this, I would probably write my own extension method to handle paging (to make the code more readable). The implementation can of course use Skip and Take:

static class PagingUtils {
  IEnumerable<T> Page(this IEnumerable<T> en, int pageSize, int page) {
    return en.Skip(page * pageSize).Take(pageSize);
  }
  IQueryable<T> Page(this IQueryable<T> en, int pageSize, int page) {
    return en.Skip(page * pageSize).Take(pageSize);
  }
}

The class defines two extension methods - one for IEnumerable and one for IQueryable, which means that you can use it with both LINQ to Objects and LINQ to SQL (when writing database query, the compiler will pick the IQueryable version).

Depending on your paging requirements, you could also add some additional behavior (for example to handle negative pageSize or page value). Here is an example how you would use this extension method in your query:

var q = (from p in products
         where p.Show == true
         select new { p.Name }).Page(10, pageIndex);
Tomas Petricek
+1 for mentioning extension methods
ram
Thank you Tomas! An example on how to use would be nice!
I added an example on how to use it.
Tomas Petricek
I believe this will return the entire result set, and then filter in-memory instead of on the server. Huge performance hit against a database if this is SQL.
jvenema
@jvenema You're right. Since this is using the `IEnumerable` interface rather than `IQueryable` this will pull in the entire database table, which will be a major performance hit.
David Pfeffer
You can of course easily add an overload for `IQueryable` to make it work with databse queries too (I editted the answer and added it). It is a bit unfortunate that you can't write the code in a fully generic way (in Haskell this would be possible with type classes). The original question mentioned LINQ to Objects, so I wrote only one overload.
Tomas Petricek