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);