怎么样.然后通过实施
本文关键字:然后 怎么样 | 更新日期: 2023-09-27 18:27:21
受此启发,我做了这个:
可存储
public interface ISortable<T>
{
IPageable<T> OrderBy<U>(Expression<Func<T, U>> orderBy);
IPageable<T> OrderByDescending<U>(Expression<Func<T, U>> orderBy);
}
可设置IPage
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 方法都是扩展方法,这是"技巧":)的一部分吗?
听起来你的类OrderedPageable
可以是Pageable
的子类,并另外实现IOrderedPageable
接口。
继承方法似乎是有意义的,因为任何处理Pageable
的东西都应该能够以相同的方式处理OrderedPageable
。
通过稍微更改接口并使用类似于上述的接口继承方法,可以使用不可变的可查询类和扩展方法实现所需的功能。
在我看来,用法更清晰,与 LINQ 更一致。
例子:
query.AsPageable(100).Page(1);
query.AsPageable(100).OrderBy(x => x.Name).ThenBy(x => x.Age).Page(1).ToList();
我还没有测试过这个,但这个概念应该有效。 请注意:
- 总项目计数是根据原始查询(而不是任何排序的查询(计算的
- 总项目计数是延迟的,只计算一次
- 根据您的查询提供程序,您可能必须公开
IPageableQuery
的SourceQuery
属性才能在PageableExtensions
中使用,因为您的查询提供程序可能无法成功转换针对此新PageableQuery
类型的查询。
接口:
public interface IPageableQuery<T> : IQueryable<T>
{
int TotalPages { get; }
int TotalItemCount { get; }
int PageSize { get; }
}
public interface IOrderedPageableQuery<T> : IPageableQuery<T>, IOrderedQueryable<T>
{
}
实现:
public class PageableQuery<T> : IPageableQuery<T>
{
readonly IQueryable<T> _sourceQuery;
readonly Lazy<int> _totalItemCount;
public int TotalPages { get { return (int)Math.Ceiling((double)TotalItemCount / PageSize); } }
public int TotalItemCount { get { return _totalItemCount.Value; } }
public int PageSize { get; private set; }
public PageableQuery(IQueryable<T> sourceQuery, int pageSize)
{
_sourceQuery = sourceQuery;
_totalItemCount = new Lazy<int>(() => _sourceQuery.Count());
PageSize = pageSize;
}
public IEnumerator<T> GetEnumerator() { return _sourceQuery.GetEnumerator();}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
public Expression Expression { get { return _sourceQuery.Expression; } }
public Type ElementType { get { return _sourceQuery.ElementType; } }
public IQueryProvider Provider { get { return _sourceQuery.Provider; } }
}
public class OrderedPageableQuery<T> : IOrderedPageableQuery<T>
{
readonly IPageableQuery<T> _sourcePageableQuery;
readonly IOrderedQueryable<T> _sourceQuery;
public int TotalPages { get { return (int)Math.Ceiling((double)TotalItemCount / PageSize); } }
public int TotalItemCount { get { return _sourcePageableQuery.TotalItemCount; } }
public int PageSize { get { return _sourcePageableQuery.PageSize; } }
public OrderedPageableQuery(IPageableQuery<T> sourcePageableQuery, IOrderedQueryable<T> newSourceQuery)
{
_sourcePageableQuery = sourcePageableQuery;
_sourceQuery = newSourceQuery;
}
public IEnumerator<T> GetEnumerator() { return _sourceQuery.GetEnumerator();}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
public Expression Expression { get { return _sourceQuery.Expression; } }
public Type ElementType { get { return _sourceQuery.ElementType; } }
public IQueryProvider Provider { get { return _sourceQuery.Provider; } }
}
扩展方法:
public static class PageableExtension
{
public static IPageableQuery<T> AsPageable<T>(this IQueryable<T> sourceQuery, int pageSize)
{
return new PageableQuery<T>(sourceQuery, pageSize);
}
public static IOrderedPageableQuery<T> OrderBy<T, U>(this IPageableQuery<T> sourcePageableQuery, Expression<Func<T, U>> orderBy)
{
return new OrderedPageableQuery<T>(sourcePageableQuery, Queryable.OrderBy(sourcePageableQuery, orderBy));
}
public static IOrderedPageableQuery<T> OrderByDescending<T, U>(this IPageableQuery<T> sourcePageableQuery, Expression<Func<T, U>> orderBy)
{
return new OrderedPageableQuery<T>(sourcePageableQuery, Queryable.OrderByDescending(sourcePageableQuery, orderBy));
}
public static IOrderedPageableQuery<T> ThenBy<T, U>(this IOrderedPageableQuery<T> sourcePageableQuery, Expression<Func<T, U>> orderBy)
{
return new OrderedPageableQuery<T>(sourcePageableQuery, Queryable.ThenBy(sourcePageableQuery, orderBy));
}
public static IOrderedPageableQuery<T> ThenByDescending<T, U>(this IOrderedPageableQuery<T> sourcePageableQuery, Expression<Func<T, U>> orderBy)
{
return new OrderedPageableQuery<T>(sourcePageableQuery, Queryable.ThenByDescending(sourcePageableQuery, orderBy));
}
public static IQueryable<T> Page<T>(this IPageableQuery<T> sourceQuery, int pageNumber)
{
return sourceQuery.Skip((pageNumber - 1) * sourceQuery.PageSize)
.Take(sourceQuery.PageSize);
}
}
我对此建议的设计是明确指出您的方法实际上是在改变源对象,并且故意不镜像 LINQ 方法名称以避免混淆。为了清楚起见,我省略了IPageable
接口和一堆其他东西,因为代码已经有点长了:
public interface ISortable<T>
{
Pageable<T> ResetOrder();
Pageable<T> AddOrder(Expression<Func<T, object>> orderBy);
Pageable<T> AddOrderDescending(Expression<Func<T, object>> orderBy);
}
public class Pageable<T> : ISortable<T>, IEnumerable<T> {
class SortKey {
public Expression<Func<T, object>> Expression { get; set; }
public bool Descending { get; set; }
}
List<SortKey> _sortKeys = new List<SortKey>();
System.Linq.IQueryable<T> _sourceQuery;
int _pageNumber;
int _pageSize;
public Pageable<T> SetPage(int pageNumber, int pageSize) {
_pageNumber = pageNumber;
_pageSize = pageSize;
return this;
}
public Pageable<T> ResetOrder()
{
_sortKeys.Clear();
return this;
}
public Pageable<T> AddOrder(Expression<Func<T, object>> orderBy)
{
_sortKeys.Add(new SortKey {
Expression = orderBy,
Descending = false
});
return this;
}
public Pageable<T> AddOrderDescending(Expression<Func<T, object>> orderBy)
{
_sortKeys.Add(new SortKey {
Expression = orderBy,
Descending = true
});
return this;
}
IEnumerable<T> SortAndPage()
{
if (_sortKeys.Count == 0)
{
return Page(_sourceQuery);
}
var firstKey = _sortKeys[0];
var orderedQuery = firstKey.Descending
? _sourceQuery.OrderByDescending(firstKey.Expression)
: _sourceQuery.OrderBy(firstKey.Expression);
foreach (var key in _sortKeys.Skip(1))
{
orderedQuery = key.Descending ? orderedQuery.ThenByDescending(key.Expression) : orderedQuery.ThenBy(key.Expression);
}
return Page(orderedQuery);
}
IEnumerable<T> Page(IQueryable<T> query)
{
return query.Skip((_pageNumber - 1) * _pageSize)
.Take (_pageSize);
}
public IEnumerator<T> GetEnumerator()
{
return SortAndPage().GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
请注意,源查询保持不变,因此您可以在枚举元素之前更改参数。如果您愿意,这也可以让您通过基于当前Pageable
返回新来实现链接行为,而不是在执行此操作的方法中this
,但它会使代码更加笨拙,因为您必须创建一个复制构造函数并添加代码来创建这些派生对象。
此外,我相信可以使用一些 FP 方法重构(或至少降低(SortAndPage()
中烦人的冗余代码,但阅读方式可能更直接。