做CURD开发的过程中,通常都会需要GetList,然而查询条件是一个可能变化的需求,如何从容对应需求变化呢?
首先,我们来设计一个套路,尝试以最小的工作量完成一次查询条件的需求变更
1.UI收集查询数据
2.UI将查询数据传递给Service
3.Service从查询配置(数据库、JSON、XML)中匹配出查询条件,并赋予UI取得的值
4.Service根据查询配置(已赋值)构建查询表达式。
5.执行查询返回数据。
大概流程如下图所示:
下面上代码,希望有人能看懂 ><
查询保存设置
public interface IEntity { int Id { get; set; } } public class QueryCondition : IEntity { [Key] public int Id { get; set; } /// <summary> /// 条件分组:以此做为查询条件 /// </summary> public string Group { get; set; } /// <summary> /// 字段名称 /// </summary> public string FieldName { get; set; } public int CompareType { get; set; } public int CompareDataType { get; set; } public string Value { get; set; } }
查询条件DTO模型
/// <summary> /// 查询结构 /// </summary> public class QueryConditionModel { public string FieldName { get; set; } public CompareType Type { get; set; } public CompareDataType DataType { get; set; } public string Value { get; set; } } public enum CompareType { Equal = 1, GreaterThan = 2, GreaterThanOrEqual = 3, LessThan = 4, LessThanOrEqual = 5, Include = 6, } public enum CompareDataType { Int = 1, String = 2, Double = 3, Decimal = 4, Float = 5, DateTime = 6 }
查询条件DTO转换配置
public class QueryConditionProfile : Profile { [Obsolete("")] protected override void Configure() { CreateMap<QueryCondition, QueryConditionModel>() .ForMember(p => p.Type, opt => { opt.MapFrom(k => (CompareType)k.CompareType); }) .ForMember(p => p.DataType, opt => { opt.MapFrom(k => (CompareDataType)k.CompareDataType); }) ; } }
查询条件构建
public class ServiceBase { protected XXXDbContext Ctx; /// <summary> /// 动态构建Lambda查询表达式 /// </summary> /// <param name="searchItems"></param> /// <returns></returns> protected Expression<Func<T, bool>> BuildExpression<T>(IList<QueryConditionModel> searchItems) { var where = PredicateExtensionses.True<T>(); if (!searchItems.Any()) return @where; foreach (var subitem in searchItems) { try { var field = subitem.FieldName; var compare = subitem.Type; var type = subitem.DataType; var value = subitem.Value; if (string.IsNullOrEmpty(field)) continue; if (string.IsNullOrEmpty(value)) continue; //构建Lambda表达式 var parameter = Expression.Parameter(typeof(T), "p"); Expression constant; //表达式左侧 like: p.Name var left = Expression.PropertyOrField(parameter, field); //表达式右侧,比较值, like '张三' var right = Expression.Constant(value); //比较表达式 switch (compare) { case CompareType.GreaterThan: constant = Expression.GreaterThan(left, right); break; case CompareType.GreaterThanOrEqual: constant = Expression.GreaterThanOrEqual(left, right); break; case CompareType.LessThan: constant = Expression.LessThan(left, right); break; case CompareType.LessThanOrEqual: constant = Expression.LessThanOrEqual(left, right); break; case CompareType.Include: //like 查询,需要调用外部int或string的Contains方法 var method = type == CompareDataType.Int ? typeof(int).GetMethod("Contains", new Type[] { typeof(int) }) : typeof(string).GetMethod("Contains", new Type[] { typeof(string) }); constant = Expression.Call(left, method, right); break; case CompareType.Equal: default: constant = Expression.Equal(left, right); break; } var lambda = Expression.Lambda<Func<T, Boolean>>(constant, parameter); @where = @where.And(lambda); } catch (Exception ex) { //OnMethodExecuted(JsonConvert.SerializeObject(searchItems), JsonConvert.SerializeObject(ex), "", LogType.Error); } } return @where; } protected Expression<Func<T, bool>> GenerateConditions<T>(Dictionary<string, string> conditions, string fieldGroup) { //read query condition define var fields = Ctx.QueryConditions.Where(p => p.Group == fieldGroup).ToList(); //read value from client conditions foreach (var condition in conditions) { SetValue(fields, condition.Key, condition.Value); } var businessCondigions = fields.Select(Mapper.Map<EntityFramework.QueryCondition, QueryConditionModel>).ToList(); return BuildExpression<T>(businessCondigions); } private void SetValue(IList<EntityFramework.QueryCondition> conditions, string name, string value) { var field = conditions.FirstOrDefault(p => p.FieldName == name); if (field == null) return; field.Value = value; } }
调用示例:
public IList<CustomerListModel> GetList(Dictionary<string,string> conditions, Pager pager) { try { var skip = (pager.PageIndex - 1) * pager.PageSize; var where = GenerateConditions<EntityFramework.Customer>(conditions, "CustomerQueryModel"); var query = Ctx.Customers.Include("MemberCard").WhereIf<EntityFramework.Customer>(where, pager); var list = query.Skip(skip).Take(pager.PageSize).ToList(); var ret = new List<CustomerListModel>(); foreach (var customer in list) { ret.Add(Mapper.Map<EntityFramework.Customer, CustomerListModel>(customer)); } //OnMethodExecuted("GetList", "", "", LogType.Operate); return ret; } catch (Exception ex) { //OnErrorThrow(JsonConvert.SerializeObject(conditions), JsonConvert.SerializeObject(ex), ex.Message); throw ex; } }