受此启发,我做了这个:
可排序
public interface ISortable<T>
{
IPageable<T> OrderBy<U>(Expression<Func<T, U>> orderBy);
IPageable<T> OrderByDescending<U>(Expression<Func<T, U>> orderBy);
}
IPageable
public interface IPageable<T> : ISortable<T>, IEnumerable<T>
{
IPageable<T> Page(int pageNumber, int pageSize);
List<T> ToList();
int TotalPages { get; }
int TotalItemCount { get; }
int PageNumber { get; }
int? PageSize { get; }
}
可分页的
public class Pageable<T> : IPageable<T>
{
private readonly IQueryable<T> _countQuery;
private IQueryable<T> _sourceQuery;
/// <summary>
/// A pageable result
/// </summary>
/// <param name="sourceQuery">Query which holdes all relevant items.</param>
public Pageable(IQueryable<T> sourceQuery)
{
_sourceQuery = sourceQuery;
_countQuery = sourceQuery;
PageNumber = 1;
}
/// <summary>
/// A pageable result
/// </summary>
/// <param name="sourceQuery">Query which holdes all relevant items.</param>
/// <param name="countQuery">
/// Alternative query optimized for counting.
/// <see cref="countQuery"/> is required to give the same count as <see cref="sourceQuery"/> else paging will break.
/// <remarks>No checks if <see cref="sourceQuery"/> and <see cref="countQuery"/> return the same count are appiled.</remarks>
/// </param>
public Pageable(IQueryable<T> sourceQuery, IQueryable<T> countQuery)
: this (sourceQuery)
{
_countQuery = countQuery;
}
#region Implementation of IEnumerable
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>
/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
/// </returns>
/// <filterpriority>1</filterpriority>
public IEnumerator<T> GetEnumerator()
{
return _sourceQuery.GetEnumerator();
}
/// <summary>
/// Returns an enumerator that iterates through a collection.
/// </summary>
/// <returns>
/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
/// </returns>
/// <filterpriority>2</filterpriority>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
#region Implementation of ISortable
public IPageable<T> OrderBy<U>(Expression<Func<T, U>> orderBy)
{
_sourceQuery = _sourceQuery.OrderBy(orderBy);
return this;
}
public IPageable<T> OrderByDescending<U>(Expression<Func<T, U>> orderBy)
{
_sourceQuery = _sourceQuery.OrderByDescending(orderBy);
return this;
}
#endregion
#region Implementation of IPageable
public int PageNumber { get; private set; }
public int? PageSize { get; private set; }
public int TotalItemCount
{
get { return _countQuery.Count(); }
}
public int TotalPages
{
get { return (int) (Math.Ceiling((double) TotalItemCount/PageSize ?? 1)); }
}
/// <summary>
/// Chop a query result into pages.
/// </summary>
/// <param name="pageNumber">Page number to fetch. Starting from 1.</param>
/// <param name="pageSize">Items per page.</param>
/// <returns></returns>
public IPageable<T> Page(int pageNumber, int pageSize)
{
PageNumber = pageNumber;
PageSize = pageSize;
_sourceQuery = _sourceQuery
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize);
return this;
}
public List<T> ToList()
{
return _sourceQuery.ToList();
}
#endregion
}
以上工作。巨大的成功!:)
但是,我在实现该方法时遇到了问题.ThenBy()
。问题是.ThenBy()
只有在.OrderBy()
被调用时才能访问。
我注意到IQueryable.OrderBy返回一个 IOrderedQueryable ,这就是访问的.ThenBy()
来源。但是为了使我当前的解决方案正常工作,我需要制作一个 IOrderedPageable 和一个新的 OrderedPagable 来配合它。OrderedPagable 将是 Pageable 的几乎完全相同的副本,这是非常糟糕的设计。
我非常怀疑这是在 LINQ 中完成的方式。所以我的问题是,他们是怎么做到的?我很好奇 :)
我确实注意到的一件事是几乎所有的 LINQ 方法都是扩展方法,这是“技巧”的一部分:)?