Me and Amr Elsehemy are working together in something and he requested a feature in some APIs I built. In fact he liked the Rob Conery's PagedList that had been modified by Troy Goode. He used it on his jBlogMVC post series about building Blog Engine using MVC.
That was pretty simple and I noticed something missing on the PagedList that I wished to add! What about sorting. I mean build a PagedList that is sorted. So I made slight changes to the enhanced PagedList made by Troy to support sorting.
Prerequisites:
It is required that you refer to Rob Conery's PagedList and the modified version by Troy Goode before you proceed in order to understand how this PagedList is useful and helpful with MVC and LINQ to SQL as well as LINQ to Entities.
Summary of changes:
To summarize the changes I made, simply I used Lambda Expressions to enable sorting. And had to modify Class declaration (with constructors) and the Initialize method. Sorting is provided by OrderBy extension method. Please return to method documentation for more details.
SortedPagedList Implementation:
Troy enhanced an interface called IPagedList introduced in Rob's post. So I am going to build a class SortedPagedList and implement IPagedList interface. For interface implementation kindly refer to the source code attached and for further explanation refer to Troy's post:
public interface IPagedList<T> : IList<T>
{
int PageCount { get; }
int TotalItemCount { get; }
int PageIndex { get; }
int PageNumber { get; }
int PageSize { get; }
bool HasPreviousPage { get; }
bool HasNextPage { get; }
bool IsFirstPage { get; }
bool IsLastPage { get; }
}
public class SortedPagedList<T, TResult> : List<T>, IPagedList<T>
{
public SortedPagedList(IEnumerable<T> source, int index, int pageSize,
System.Linq.Expressions.Expression<Func<T, TResult>> keySelector, bool asc)
{
if( source is IQueryable<T> )
Initialize( source as IQueryable<T>, index, pageSize, keySelector, asc );
else
Initialize(source.AsQueryable(), index, pageSize, keySelector, asc);
}
public SortedPagedList(IQueryable<T> source, int index, int pageSize,
System.Linq.Expressions.Expression<Func<T, TResult>> keySelector, bool asc)
{
Initialize(source, index, pageSize, keySelector, asc);
}
}
As you noticed I am using predicate Expression<Func<T,TRestult>> function in the constructor declaration and on Initialize method call. Basically this predicate is a strongly typed lambda expression to test each element for a condition. You can pass a Lambda Expression here. So the usage would be like this:
SortedPagedList prods=SortedPagedList<T,TResult>(source,index,pageSize,p=>p.Price,true);
The last parameter is to specify if you want to sort Ascending or Descending.
Initilaize method implementation is the same but it has a slight changes as the following:
protected void Initialize(IQueryable<T> source, int index, int pageSize,
System.Linq.Expressions.Expression<Func<T, TResult>> keySelector, bool asc)
{
// Method code omitted......
//### add items to internal list
if (TotalItemCount > 0)
{
if (asc)
{
AddRange( source.OrderBy(keySelector).Skip(index * pageSize).Take(pageSize));
}
else
{
AddRange(source.OrderByDescending(keySelector).Skip(index * pageSize).Take(pageSize));
}
}
}
Attached source code include a test class made with xUnit.Net. Also I've tested it against LINQ to Entities and I might post a sample using it soon so keep tuned.
