Paging through lists with LINQ
January 30, 2010There are a number of fantastic LINQ extension methods that extend IEnumerable(T). I'm going to be using Skip(Tsource) and Take(Tsource) to implement paging of a large list of data in as little as one line of code.
Let's say we have a list of integers from 1 to 9:
IEnumerable<int> list1 = new List<int> {1,2,3,4,5,6,7,8,9};
With skip(n) we can skip the first n items in the list, returning the same ienumerable<int> in our case:
var skipping = list1.skip(3); //4,5,6,7,8,9
With take(n) we can "take" the first n items in the list, omitting the rest:
var taking = list1.take(2); //1,2
And so when chaining both together, we can narrow in on a subset (or page, as we'll see later) of the list:
var both = list1.skip(3).take(2); //4,5
Implementing a paging mechanism in an mvc view page
To implement a paging mechanism on a large set of ienumerable data requires mostly boilerplate code. the only additional pieces of information we need to know is the number of items to display per page and what the current page number is (defaulting to 1, of course). let's use the following 100-item list (of string) as our source data
item 0
item 1
item 2
...
item 99
and construct an action method that accepts a page number parameter, using a fixed 5 items per page limit.
public actionresult index(int page)
{
int itemsperpage = 5;
ienumerable<string> list = get100itemlistsomehow();
list = list.skip((page - 1) * itemsperpage).take(itemsperpage);
return view(list);
}
Assuming a page value of 1 was requested (a url something like http://mysite/home/index?page=1 in this case) our list would be reduced to:
item 0
item 1
item 2
item 3
item 4
A more robust implementation
The following example is something similar to what i've done on my website home page to display and page through a 10 page list of RSS feed items. I've defined a class called pager that encapsulates most of the paging logic and exposes properties like nextpage, previouspage, etc. it's also used as the model-bound object passed into the Action method and picks up the url parameters page and perPage if present.
This is not necessarily an ideal implementation in every scenario - i've only added the most basic check's and balances in the pager class and i'm leaving it up to the View to determine whether a previous or next button needs to be displayed (in this example, they are always displayed). But it's a pretty solid starting point.
The pager class
public class pager
{
private int _page;
public int page
{
get { return _page < 1 ? 1 : _page; }
set { _page = value; }
}
private int _perpage;
public int perpage
{
get { return _perpage < 1 ? 10 : _perpage; }
set { _perpage = value; }
}
public int itemcount { get; set; }
public int previouspage
{
get { return page - 1; }
}
public int nextpage
{
get { return (page == lastpage) ? page : page + 1; }
}
public int lastpage
{
get { return (int)math.ceiling((itemcount / (double)perpage)); }
}
}
Controller action method
public actionresult index(pager pager)
{
var list = mylist.get(); //list of 100 strings (eg. "Item 0", and so on)
pager.itemcount = list.count(); //pager needs to know item count
list = list.skip((pager.page - 1) * pager.perpage).take(pager.perpage); //perform operation
return view(new homeindexviewmodel{ list = list, pager = pager });
}
The View markup
<%=Html.ActionLink("Previous", "Index", new { page = Model.Pager.PreviousPage }) %> |
<%=Html.ActionLink("Next", "Index", new { page = Model.Pager.NextPage })%>
<br/>
<%foreach (var item in Model.List) { %>
<%=item %>
<%} %>
And some examples of the output
Optionally specify a different amount per page http://mysite/?page=24&perpage=3
Tags: skip, take, ienumerable, extension method
Categories: ASP.NET MVC, C#, Linq

View Comments & Reactions >>