信息系統的查詢需求變幻無窮,在倉儲中爲每一個查詢需求建立一個特殊方法,將致使大量乏味而臃腫的接口。express
一種更加可行的辦法是,在應用層服務中描述查詢需求,並經過倉儲執行查詢。框架
爲了可以更好的描述查詢需求,能夠將查詢功能從倉儲中抽取出來,專門建立一個查詢對象。ui
查詢最複雜的部分是條件過濾,這也是查詢對象的主要職責。查詢對象能夠認爲是規約模式的一個變種,容許查詢對象動態建立查詢條件。this
在Util.Domains項目Repositories目錄中,建立查詢對象基接口IQueryBase,代碼以下。spa
using System; using System.Linq.Expressions; namespace Util.Domains.Repositories { /// <summary>
/// 查詢對象 /// </summary>
/// <typeparam name="TEntity">實體類型</typeparam>
public interface IQueryBase<TEntity> : IPager where TEntity : class, IAggregateRoot { /// <summary>
/// 獲取謂詞 /// </summary>
Expression<Func<TEntity, bool>> GetPredicate(); /// <summary>
/// 獲取排序 /// </summary>
string GetOrderBy(); } }
IQueryBase接口主要用來支持將查詢對象傳入倉儲中。code
在倉儲接口IRepository中增長兩個方法,代碼以下。對象
/// <summary>
/// 查詢 /// </summary>
/// <param name="query">查詢對象</param>
IQueryable<TEntity> Query( IQueryBase<TEntity> query ); /// <summary>
/// 分頁查詢 /// </summary>
/// <param name="query">查詢對象</param>
PagerList<TEntity> PagerQuery( IQueryBase<TEntity> query );
在倉儲類Repository中實現這兩個方法,代碼以下。blog
/// <summary>
/// 查詢 /// </summary>
/// <param name="query">查詢對象</param>
public IQueryable<TEntity> Query( IQueryBase<TEntity> query ) { return FilterBy( Find(), query ); } /// <summary>
/// 過濾 /// </summary>
protected IQueryable<TEntity> FilterBy( IQueryable<TEntity> queryable, IQueryBase<TEntity> query ) { var predicate = query.GetPredicate(); if ( predicate == null ) return queryable; return queryable.Where( predicate ); } /// <summary>
/// 分頁查詢 /// </summary>
/// <param name="query">查詢對象</param>
public virtual PagerList<TEntity> PagerQuery( IQueryBase<TEntity> query ) { return Query( query ).PagerResult( query ); }
以上代碼完成了將查詢對象傳入倉儲並獲取結果,下面開始實現查詢對象。排序
在Util.Datas項目Queries目錄中,建立IQuery接口,代碼以下。接口
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using Util.Domains; using Util.Domains.Repositories; namespace Util.Datas.Queries { /// <summary>
/// 查詢對象 /// </summary>
/// <typeparam name="TEntity">實體類型</typeparam>
/// <typeparam name="TKey">實體標識類型</typeparam>
public interface IQuery<TEntity, TKey> : IQueryBase<TEntity> where TEntity : class,IAggregateRoot<TKey> { /// <summary>
/// 添加謂詞,僅能添加一個條件,若是參數值爲空,則忽略該條件 /// </summary>
/// <param name="predicate">謂詞</param>
/// <param name="isOr">是否使用Or鏈接</param>
IQuery<TEntity, TKey> Filter( Expression<Func<TEntity, bool>> predicate, bool isOr = false ); /// <summary>
/// 過濾條件 /// </summary>
/// <param name="propertyName">屬性名</param>
/// <param name="value">值</param>
/// <param name="operator">運算符</param>
IQuery<TEntity,TKey> Filter( string propertyName, object value, Operator @operator = Operator.Equal ); /// <summary>
/// 添加查詢條件 /// </summary>
/// <param name="criteria">查詢條件</param>
IQuery<TEntity,TKey> Filter( ICriteria<TEntity> criteria ); /// <summary>
/// 過濾int數值段 /// </summary>
/// <typeparam name="TProperty">屬性類型</typeparam>
/// <param name="propertyExpression">屬性表達式,範例:t => t.Age</param>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
IQuery<TEntity,TKey> FilterInt<TProperty>( Expression<Func<TEntity, TProperty>> propertyExpression, int? min,int? max ); /// <summary>
/// 過濾double數值段 /// </summary>
/// <typeparam name="TProperty">屬性類型</typeparam>
/// <param name="propertyExpression">屬性表達式,範例:t => t.Age</param>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
IQuery<TEntity,TKey> FilterDouble<TProperty>( Expression<Func<TEntity, TProperty>> propertyExpression, double? min, double? max ); /// <summary>
/// 過濾日期段,不包含時間 /// </summary>
/// <typeparam name="TProperty">屬性類型</typeparam>
/// <param name="propertyExpression">屬性表達式,範例:t => t.Age</param>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
IQuery<TEntity,TKey> FilterDate<TProperty>( Expression<Func<TEntity, TProperty>> propertyExpression, DateTime? min, DateTime? max ); /// <summary>
/// 過濾日期時間段,包含時間 /// </summary>
/// <typeparam name="TProperty">屬性類型</typeparam>
/// <param name="propertyExpression">屬性表達式,範例:t => t.Age</param>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
IQuery<TEntity,TKey> FilterDateTime<TProperty>( Expression<Func<TEntity, TProperty>> propertyExpression, DateTime? min, DateTime? max ); /// <summary>
/// 過濾decimal數值段 /// </summary>
/// <typeparam name="TProperty">屬性類型</typeparam>
/// <param name="propertyExpression">屬性表達式,範例:t => t.Age</param>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
IQuery<TEntity,TKey> FilterDecimal<TProperty>( Expression<Func<TEntity, TProperty>> propertyExpression, decimal? min, decimal? max ); /// <summary>
/// 與鏈接,將傳入的查詢條件合併到當前對象 /// </summary>
/// <param name="query">查詢對象</param>
IQuery<TEntity,TKey> And( IQuery<TEntity,TKey> query ); /// <summary>
/// 與鏈接,將傳入的查詢條件合併到當前對象 /// </summary>
/// <param name="predicate">謂詞</param>
IQuery<TEntity,TKey> And( Expression<Func<TEntity, bool>> predicate ); /// <summary>
/// 或鏈接,將傳入的查詢條件合併到當前對象 /// </summary>
/// <param name="query">查詢對象</param>
IQuery<TEntity,TKey> Or( IQuery<TEntity,TKey> query ); /// <summary>
/// 或鏈接,將傳入的查詢條件合併到當前對象 /// </summary>
/// <param name="predicate">謂詞</param>
IQuery<TEntity,TKey> Or( Expression<Func<TEntity, bool>> predicate ); /// <summary>
/// 添加排序,支持屢次調用OrderBy建立多級排序 /// </summary>
/// <typeparam name="TProperty">屬性類型</typeparam>
/// <param name="expression">屬性表達式</param>
/// <param name="desc">是否降序</param>
IQuery<TEntity,TKey> OrderBy<TProperty>( Expression<Func<TEntity, TProperty>> expression, bool desc = false ); /// <summary>
/// 添加排序,支持屢次調用OrderBy建立多級排序 /// </summary>
/// <param name="propertyName">排序屬性</param>
/// <param name="desc">是否降序</param>
IQuery<TEntity,TKey> OrderBy( string propertyName, bool desc = false ); /// <summary>
/// 獲取列表 /// </summary>
/// <param name="queryable">數據源</param>
List<TEntity> GetList( IQueryable<TEntity> queryable ); /// <summary>
/// 獲取分頁列表 /// </summary>
/// <param name="queryable">數據源</param>
PagerList<TEntity> GetPagerList( IQueryable<TEntity> queryable ); } }
查詢對象中定義的過濾方法默認都是以And方式鏈接,因此須要增長專門的Or鏈接方法。當查詢需求比較複雜時,能夠建立多個查詢對象進行合併,從而建立更爲複雜的查詢條件。
爲了使用方便,查詢對象自己還提供了獲取數據的方法,須要傳入IQueryable 對象,以便執行實際的查詢操做。
在Util.Datas項目Queries目錄中,建立查詢對象實現類Query,代碼以下。
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using Util.Datas.Queries.Criterias; using Util.Datas.Queries.OrderBys; using Util.Domains; using Util.Domains.Repositories; namespace Util.Datas.Queries { /// <summary>
/// 查詢對象 /// </summary>
/// <typeparam name="TEntity">實體類型</typeparam>
/// <typeparam name="TKey">實體標識類型</typeparam>
public class Query<TEntity, TKey> : Pager, IQuery<TEntity, TKey> where TEntity : class ,IAggregateRoot<TKey> { #region 構造方法
/// <summary>
/// 初始化查詢對象 /// </summary>
public Query() { OrderBuilder = new OrderByBuilder(); } /// <summary>
/// 初始化查詢對象 /// </summary>
/// <param name="pager">分頁對象</param>
public Query( IPager pager ) : this() { Page = pager.Page; PageSize = pager.PageSize; TotalCount = pager.TotalCount; OrderBy( pager.Order ); } #endregion
#region 屬性
/// <summary>
/// 查詢條件 /// </summary>
private ICriteria<TEntity> Criteria { get; set; } /// <summary>
/// 排序生成器 /// </summary>
private OrderByBuilder OrderBuilder { get; set; } #endregion
#region GetPredicate(獲取謂詞)
/// <summary>
/// 獲取謂詞 /// </summary>
public Expression<Func<TEntity, bool>> GetPredicate() { if ( Criteria == null ) return null; return Criteria.GetPredicate(); } #endregion
#region GetOrderBy(獲取排序)
/// <summary>
/// 獲取排序 /// </summary>
public string GetOrderBy() { Order = OrderBuilder.Generate(); if ( string.IsNullOrWhiteSpace( Order ) ) Order = "Id desc"; return Order; } #endregion
#region 過濾條件
/// <summary>
/// 添加謂詞,僅能添加一個條件,若是參數值爲空,則忽略該條件 /// </summary>
/// <param name="predicate">謂詞</param>
/// <param name="isOr">是否使用Or鏈接</param>
public IQuery<TEntity, TKey> Filter( Expression<Func<TEntity, bool>> predicate,bool isOr = false ) { predicate = QueryHelper.ValidatePredicate( predicate ); if ( predicate == null ) return this; if ( isOr ) Or( predicate ); else And( predicate ); return this; } /// <summary>
/// 過濾條件 /// </summary>
/// <param name="propertyName">屬性名</param>
/// <param name="value">值</param>
/// <param name="operator">運算符</param>
public IQuery<TEntity, TKey> Filter( string propertyName, object value, Operator @operator = Operator.Equal ) { return Filter( Lambda.ParsePredicate<TEntity>( propertyName, value, @operator ) ); } /// <summary>
/// 添加查詢條件 /// </summary>
/// <param name="criteria">查詢條件</param>
public IQuery<TEntity, TKey> Filter( ICriteria<TEntity> criteria ) { And( criteria.GetPredicate() ); return this; } /// <summary>
/// 過濾int數值段 /// </summary>
/// <typeparam name="TProperty">屬性類型</typeparam>
/// <param name="propertyExpression">屬性表達式,範例:t => t.Age</param>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
public IQuery<TEntity, TKey> FilterInt<TProperty>( Expression<Func<TEntity, TProperty>> propertyExpression, int? min, int? max ) { return Filter( new IntSegmentCriteria<TEntity, TProperty>( propertyExpression, min, max ) ); } /// <summary>
/// 過濾double數值段 /// </summary>
/// <typeparam name="TProperty">屬性類型</typeparam>
/// <param name="propertyExpression">屬性表達式,範例:t => t.Age</param>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
public IQuery<TEntity, TKey> FilterDouble<TProperty>( Expression<Func<TEntity, TProperty>> propertyExpression, double? min, double? max ) { return Filter( new DoubleSegmentCriteria<TEntity, TProperty>( propertyExpression, min, max ) ); } /// <summary>
/// 過濾日期段,不包含時間 /// </summary>
/// <typeparam name="TProperty">屬性類型</typeparam>
/// <param name="propertyExpression">屬性表達式,範例:t => t.Age</param>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
public IQuery<TEntity, TKey> FilterDate<TProperty>( Expression<Func<TEntity, TProperty>> propertyExpression, DateTime? min, DateTime? max ) { return Filter( new DateSegmentCriteria<TEntity, TProperty>( propertyExpression, min, max ) ); } /// <summary>
/// 過濾日期時間段,包含時間 /// </summary>
/// <typeparam name="TProperty">屬性類型</typeparam>
/// <param name="propertyExpression">屬性表達式,範例:t => t.Age</param>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
public IQuery<TEntity, TKey> FilterDateTime<TProperty>( Expression<Func<TEntity, TProperty>> propertyExpression, DateTime? min, DateTime? max ) { return Filter( new DateTimeSegmentCriteria<TEntity, TProperty>( propertyExpression, min, max ) ); } /// <summary>
/// 過濾decimal數值段 /// </summary>
/// <typeparam name="TProperty">屬性類型</typeparam>
/// <param name="propertyExpression">屬性表達式,範例:t => t.Age</param>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
public IQuery<TEntity, TKey> FilterDecimal<TProperty>( Expression<Func<TEntity, TProperty>> propertyExpression, decimal? min, decimal? max ) { return Filter( new DecimalSegmentCriteria<TEntity, TProperty>( propertyExpression, min, max ) ); } #endregion
#region 鏈接
/// <summary>
/// 與鏈接,將傳入的查詢條件合併到當前對象 /// </summary>
/// <param name="query">查詢對象</param>
public IQuery<TEntity, TKey> And( IQuery<TEntity, TKey> query ) { return And( query.GetPredicate() ); } /// <summary>
/// 與鏈接,將傳入的查詢條件合併到當前對象 /// </summary>
/// <param name="predicate">謂詞</param>
public IQuery<TEntity, TKey> And( Expression<Func<TEntity, bool>> predicate ) { if ( Criteria == null ) { Criteria = new Criteria<TEntity>( predicate ); return this; } Criteria = new AndCriteria<TEntity>( Criteria.GetPredicate(), predicate ); return this; } /// <summary>
/// 或鏈接,將傳入的查詢條件合併到當前對象 /// </summary>
/// <param name="query">查詢對象</param>
public IQuery<TEntity, TKey> Or( IQuery<TEntity, TKey> query ) { return Or( query.GetPredicate() ); } /// <summary>
/// 或鏈接,將傳入的查詢條件合併到當前對象 /// </summary>
/// <param name="predicate">謂詞</param>
public IQuery<TEntity, TKey> Or( Expression<Func<TEntity, bool>> predicate ) { if ( Criteria == null ) { Criteria = new Criteria<TEntity>( predicate ); return this; } Criteria = new OrCriteria<TEntity>( Criteria.GetPredicate(), predicate ); return this; } #endregion
#region OrderBy(排序)
/// <summary>
/// 添加排序,支持屢次調用OrderBy建立多級排序 /// </summary>
/// <typeparam name="TProperty">屬性類型</typeparam>
/// <param name="expression">屬性表達式</param>
/// <param name="desc">是否降序</param>
public IQuery<TEntity, TKey> OrderBy<TProperty>( Expression<Func<TEntity, TProperty>> expression, bool desc = false ) { return OrderBy( Lambda.GetName( expression ), desc ); } /// <summary>
/// 添加排序,支持屢次調用OrderBy建立多級排序 /// </summary>
/// <param name="propertyName">排序屬性</param>
/// <param name="desc">是否降序</param>
public IQuery<TEntity, TKey> OrderBy( string propertyName, bool desc = false ) { OrderBuilder.Add( propertyName, desc ); GetOrderBy(); return this; } #endregion
#region Clear(清理)
/// <summary>
/// 清理 /// </summary>
public void Clear() { Criteria = null; OrderBuilder = new OrderByBuilder(); } #endregion
#region GetList(獲取列表)
/// <summary>
/// 獲取列表 /// </summary>
/// <param name="queryable">數據源</param>
public List<TEntity> GetList( IQueryable<TEntity> queryable ) { return Execute( queryable ).OrderBy( Order ).ToList(); } /// <summary>
/// 執行過濾和分頁 /// </summary>
private IQueryable<TEntity> Execute( IQueryable<TEntity> queryable ) { queryable.CheckNull( "queryable" ); queryable = FilterBy( queryable ); GetOrderBy(); return queryable; } /// <summary>
/// 過濾 /// </summary>
private IQueryable<TEntity> FilterBy( IQueryable<TEntity> queryable ) { if ( Criteria == null ) return queryable; return queryable.Where( Criteria.GetPredicate() ); } #endregion
#region GetPagerList(獲取分頁列表)
/// <summary>
/// 獲取分頁列表 /// </summary>
/// <param name="queryable">數據源</param>
public PagerList<TEntity> GetPagerList( IQueryable<TEntity> queryable ) { return Execute( queryable ).PagerResult( this ); } #endregion } }
應用層服務能夠這樣查詢。
public PagerList<ApplicationDto> Query( ApplicationQuery query ) { return new Query<Application>(query) .Filter( t => t.Code == query.Code ) .Filter( t => t.Name.Contains( query.Name ) ) .Filter( t => t.Enabled == query.Enabled ) .FilterDate( t => t.CreateTime, query.BeginCreateTime, query.EndCreateTime ) .GetPagerList( Repository.Find() ).Convert( t => t.ToDto() ); }
或
public PagerList<ApplicationDto> Query2( ApplicationQuery query ) { var queryObject = new Query<Application>( query ) .Filter( t => t.Code == query.Code ) .Filter( t => t.Name.Contains( query.Name ) ) .Filter( t => t.Enabled == query.Enabled ) .FilterDate( t => t.CreateTime, query.BeginCreateTime, query.EndCreateTime ); return Repository.PagerQuery( queryObject ).Convert( t => t.ToDto() ); }
.Net應用程序框架交流QQ羣: 386092459,歡迎有興趣的朋友加入討論。
謝謝你們的持續關注,個人博客地址:http://www.cnblogs.com/xiadao521/