使用Extensions动态生成查询表达式树
根据自我项目的情况以及园子里高手的博文整理出的帮助,希望能帮助到大家。
1、场景介绍
前端按照列表中的字段动态配置筛选条件,要求后台也要根据不同的查询条件动态分配。
2、前后端约定
这部分使用字段 searchVal和searchVal_FilterMode和searchFormInputAttrs定义。如下
/// <summary> /// 关键字模糊查询 /// </summary> public string searchVal { get; set; } /// <summary> /// 通用查询配置 (1:模糊查询 , 2:精准查询) /// </summary> public int searchVal_FilterMode { get; set; } /// <summary> /// 子搜索组件传递的通用查询配置的属性名 /// </summary> public List<string> searchFormInputAttrs { get; set; }
高级查询部分,要求筛选模式使用后缀 _FilterMode,如下示例
/// <summary> /// 工单号 /// </summary> public string WorkOrderNo { get; set; } /// <summary> /// 工单号-查询过滤模式 /// </summary> public string WorkOrderNo_FilterMode { get; set; }
如果是日期范围的话,使用List<String> 形式,如下示例
#region 单独处理日期 //表示是 高级查询范围查询特性 [HighSearchRangeAttribute] public List<string> CreateTime { get; set; } #endregion
/// <summary> /// 高级查询范围查询特性 /// </summary> public class HighSearchRangeAttribute : Attribute { }
配置的枚举类
/// <summary> /// 通用查询配置模式 /// </summary> public enum SearchFilterModeEnum { 模糊查询 = 1, 精准查询 = 2, 空值 = 3, 大于等于 = 4, 小于等于 = 5, 大于 = 6, 小于 = 7, 不等于 = 8 }
3、后端处理
生成Where条件扩展类
/// <summary> /// 生成Where条件扩展 /// </summary> public class WhereConditionsExtensions { public static FunReturnResultModel<Expression<Func<T, bool>>> GetWhereConditions<T, TParam>(TParam param) { FunReturnResultModel<Expression<Func<T, bool>>> result = new FunReturnResultModel<Expression<Func<T, bool>>>(); try { Expression<Func<T, bool>> conditions = PredicateExtensions.True<T>(); var errMsg = ""; #region 1、通用查询searchVal //判断通用查询searchVal var pro_value_searchVal = ClassHelper.GetPropertyValue<TParam>(param, SystemCommonVar.searchVal, out errMsg); if (!string.IsNullOrEmpty(errMsg)) { result.IsSuccess = false; result.ErrMsg = errMsg; return result; } if (!string.IsNullOrEmpty(pro_value_searchVal)) {//表示 通用查询searchVal不为空,需要查询 var pro_value_searchValMode = ClassHelper.GetPropertyValue<TParam>(param, SystemCommonVar.searchVal_FilterMode, out errMsg); if (!string.IsNullOrEmpty(errMsg)) { result.IsSuccess = false; result.ErrMsg = errMsg; return result; } var pro_value_searchFormInputAttrs = ClassHelper.GetPropertyValueForList<TParam>(param, SystemCommonVar.searchFormInputAttrs, out errMsg); if (!string.IsNullOrEmpty(errMsg)) { result.IsSuccess = false; result.ErrMsg = errMsg; return result; } if (pro_value_searchFormInputAttrs != null && pro_value_searchFormInputAttrs.Count > 0) { var i_pro_value_searchValMode = Convert.ToInt32(pro_value_searchValMode); if (i_pro_value_searchValMode == Convert.ToInt32(SearchFilterModeEnum.模糊查询)) {//模糊查询 var new_conditions = PredicateExtensions.GetConditionExpressionForMoreField<T>(pro_value_searchFormInputAttrs.ToArray(), pro_value_searchVal); conditions = conditions.And(new_conditions); } else if (i_pro_value_searchValMode == Convert.ToInt32(SearchFilterModeEnum.精准查询)) {//精准查询 var new_conditions = PredicateExtensions.GetConditionExpressionForMoreFieldByAnd<T>(pro_value_searchFormInputAttrs.ToArray(), pro_value_searchVal); conditions = conditions.And(new_conditions); /* pro_value_searchFormInputAttrs.ForEach(x => { if (!string.IsNullOrEmpty(x)) { var myParam = Expression.Parameter(typeof(T)); var condition = Expression.Lambda<Func<T, bool>>( Expression.Equal( Expression.Property(myParam, x), Expression.Constant(pro_value_searchVal, typeof(string)) ), myParam ); // for LINQ to SQl/Entities skip Compile() call conditions = conditions.And(condition); } }); //*/ } else { result.IsSuccess = false; result.ErrMsg = "通用查询配置不正确"; return result; } } } #endregion #region 2、高级查询 var high_pros = FilterHigh_pros<TParam>(param); List<HighSearchForDateTimeRangeModel> hsmForDatetimeList = new List<HighSearchForDateTimeRangeModel>(); List<HighSearchModel> hsmList = GetHighSearchModelList<TParam>(param, high_pros, ref hsmForDatetimeList); if (hsmList.Count > 0) { var high_conditions = PredicateExtensions.GetConditionExpressionForHighFieldByAnd<T>(hsmList); conditions = conditions.And(high_conditions); } if (hsmForDatetimeList.Count > 0) { var high_conditions = PredicateExtensions.GetConditionExpressionForHighFieldByAnd<T>(hsmForDatetimeList); conditions = conditions.And(high_conditions); } #endregion result.data = conditions; result.IsSuccess = true; return result; } catch { throw; } } /// <summary> /// 过滤掉Mode后缀的属性 和一些特殊属性的,如DateTimeRange /// </summary> /// <typeparam name="TParam"></typeparam> /// <param name="param"></param> /// <param name="high_pros"></param> /// <returns></returns> private static List<HighSearchModel> GetHighSearchModelList<TParam>(TParam param, List<PropertyInfo> high_pros, ref List<HighSearchForDateTimeRangeModel> hsmForDatetimeList) { List<HighSearchModel> hsmList = new List<HighSearchModel>(); string errMsg = ""; SearchFilterModeEnum _svmEnum = default(SearchFilterModeEnum); var hsmValue = ""; var pro_value = ""; foreach (var pro in high_pros) { if (!pro.Name.Contains(SystemCommonVar.highSearchModeSuffix)) {//不是Mode后缀的字段 //判断是不是有特性特性的值 var Attributes = ClassHelper.GetDIYAttributesByPro(pro); if (Attributes.Length > 0) { object v = ClassHelper.GetPropertyValue<TParam>(param, pro); if (v == null) { continue; } List<string> arr_value = v as List<string>; if (arr_value.Count < 2) { continue; } //string[] arr_value = (string[])v; //string[] arr_value = Convert.ToString(v).Split(','); hsmForDatetimeList.Add(new HighSearchForDateTimeRangeModel() { fieldName = pro.Name, start_fieldValue = arr_value[0], end_fieldValue = arr_value[1] }); continue; } pro_value = ClassHelper.GetPropertyValueByObject<TParam>(param, pro); if (string.IsNullOrEmpty(pro_value)) { continue; } //查找是相应Mode后缀的字段的值 hsmValue = ClassHelper.GetPropertyValue<TParam>(param, pro.Name + SystemCommonVar.highSearchModeSuffix, out errMsg); if (!string.IsNullOrEmpty(errMsg)) { throw new Exception(errMsg); } if (hsmValue == "0" || string.IsNullOrEmpty(hsmValue)) { continue; } _svmEnum = (SearchFilterModeEnum)Enum.Parse(typeof(SearchFilterModeEnum), hsmValue); if (_svmEnum == SearchFilterModeEnum.空值) { continue; } hsmList.Add(new HighSearchModel() { fieldName = pro.Name, fieldValue = pro_value, filterMode = _svmEnum }); } } return hsmList; } /// <summary> /// 过滤一些不需要的字段,如Page、PageSize等 /// </summary> /// <typeparam name="TParam"></typeparam> /// <param name="param"></param> /// <returns></returns> private static List<PropertyInfo> FilterHigh_pros<TParam>(TParam param) { var all_high_pros = ClassHelper.GetPropertyInfoList<TParam>(param); var high_pros = new List<PropertyInfo>(); //排除不用的 for (int i = 0; i < all_high_pros.Length; i++) { if (!SystemCommonVar.commnParamFatherPros.Contains(all_high_pros[i].Name)) { high_pros.Add(all_high_pros[i]); } } return high_pros; } }
public class ParameterRebinder : ExpressionVisitor { private readonly Dictionary<ParameterExpression, ParameterExpression> map; public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map) { this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>(); } public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp) { return new ParameterRebinder(map).Visit(exp); } protected override Expression VisitParameter(ParameterExpression p) { ParameterExpression replacement; if (map.TryGetValue(p, out replacement)) { p = replacement; } return base.VisitParameter(p); } } public static class PredicateExtensions { public static Expression<Func<T, bool>> True<T>() { return f => true; } public static Expression<Func<T, bool>> False<T>() { return f => false; } public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge) { // build parameter map (from parameters of second to parameters of first) var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f); // replace parameters in the second lambda expression with parameters from the first var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); // apply composition of lambda expression bodies to parameters from the first expression return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters); } public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) { return first.Compose(second, Expression.And); } public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) { return first.Compose(second, Expression.Or); } /// <summary> /// 拼接成 c.Name.contains("1111")||c.Name.Contains("2222")||c.Name.Contains("3333")) 形式 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="options"></param> /// <param name="fieldName"></param> /// <returns></returns> public static Expression<Func<T, bool>> GetConditionExpression<T>(string[] options, string fieldName) { ParameterExpression left = Expression.Parameter(typeof(T), "c");//c=> Expression expression = Expression.Constant(false); foreach (var optionName in options) { Expression right = Expression.Call ( Expression.Property(left, typeof(T).GetProperty(fieldName)), //c.DataSourceName typeof(string).GetMethod("Contains", new Type[] { typeof(string) }),// 反射使用.Contains()方法 Expression.Constant(optionName) // .Contains(optionName) ); expression = Expression.Or(right, expression);//c.DataSourceName.contain("") || c.DataSourceName.contain("") } Expression<Func<T, bool>> finalExpression = Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { left }); return finalExpression; } /// <summary> /// 拼接成 c.Name.contains("1111")||c.Code.Contains("1111")||c.Address.Contains("1111")) 形式 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="options"></param> /// <param name="fieldName"></param> /// <returns></returns> public static Expression<Func<T, bool>> GetConditionExpressionForMoreField<T>(string[] fieldNames, string fieldValue) { ParameterExpression left = Expression.Parameter(typeof(T), "c");//c=> Expression expression = Expression.Constant(false); foreach (var fieldName in fieldNames) { Expression right = Expression.Call ( Expression.Property(left, typeof(T).GetProperty(fieldName)), //c.DataSourceName typeof(string).GetMethod("Contains", new Type[] { typeof(string) }),// 反射使用.Contains()方法 Expression.Constant(fieldValue) // .Contains(fieldValue) ); expression = Expression.Or(right, expression);//c.AAA.contain("") || c.BBB.contain("") } Expression<Func<T, bool>> finalExpression = Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { left }); return finalExpression; } /// <summary> /// 拼接成 c.Name.equals("1111")||c.Code.equals("1111")||c.Address.equals("1111")) 形式 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="options"></param> /// <param name="fieldName"></param> /// <returns></returns> public static Expression<Func<T, bool>> GetConditionExpressionForMoreFieldByAnd<T>(string[] fieldNames, string fieldValue) { ParameterExpression left = Expression.Parameter(typeof(T), "c");//c=> Expression expression = Expression.Constant(false); foreach (var fieldName in fieldNames) { Expression right = Expression.Call ( Expression.Property(left, typeof(T).GetProperty(fieldName)), //c.DataSourceName typeof(string).GetMethod("Equals", new Type[] { typeof(string) }),// 反射使用.Contains()方法 Expression.Constant(fieldValue) // .Contains(fieldValue) ); expression = Expression.Or(right, expression);//c.AAA.contain("") || c.BBB.contain("") } Expression<Func<T, bool>> finalExpression = Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { left }); return finalExpression; } public static Expression<Func<T, bool>> GetConditionExpressionForHighFieldByAnd<T>(List<HighSearchModel> hsmList) { var whereHelper = new WhereHelper<T>(); foreach (var field in hsmList) { switch (field.filterMode) { case Model.Enums.SearchFilterModeEnum.不等于: whereHelper.NotEqual(field.fieldName, field.fieldValue); break; case Model.Enums.SearchFilterModeEnum.大于: whereHelper.GreaterThan(field.fieldName, field.fieldValue); break; case Model.Enums.SearchFilterModeEnum.大于等于: whereHelper.GreaterThanOrEqual(field.fieldName, field.fieldValue); break; case Model.Enums.SearchFilterModeEnum.小于: whereHelper.LessThan(field.fieldName, field.fieldValue); break; case Model.Enums.SearchFilterModeEnum.小于等于: whereHelper.LessThanOrEqual(field.fieldName, field.fieldValue); break; case Model.Enums.SearchFilterModeEnum.模糊查询: whereHelper.Contains(field.fieldName, field.fieldValue); break; case Model.Enums.SearchFilterModeEnum.精准查询: whereHelper.Equal(field.fieldName, field.fieldValue); break; } } Expression<Func<T, bool>> finalExpression = Expression.Lambda<Func<T, bool>>(whereHelper.filter, new ParameterExpression[] { whereHelper.param }); return finalExpression; } public static Expression<Func<T, bool>> GetConditionExpressionForHighFieldByAnd<T>(List<HighSearchForDateTimeRangeModel> hsmForDatetimeList) { var whereHelper = new WhereHelper<T>(); foreach (var field in hsmForDatetimeList) { whereHelper.GreaterThanOrEqual(field.fieldName, field.start_fieldValue); whereHelper.LessThanOrEqual(field.fieldName, field.end_fieldValue); } Expression<Func<T, bool>> finalExpression = Expression.Lambda<Func<T, bool>>(whereHelper.filter, new ParameterExpression[] { whereHelper.param }); return finalExpression; } }
public class WhereHelper<T> //where T : class { public ParameterExpression param; public BinaryExpression filter; private MemberExpression common_left; private UnaryExpression common_right; public WhereHelper() { param = Expression.Parameter(typeof(T), "c");//构建 c=> 结构 //1==1 Expression left = Expression.Constant(1); filter = Expression.Equal(left, left);//构建出 c=> 1=1 } public Expression<Func<T, bool>> GetExpression() { return Expression.Lambda<Func<T, bool>>(filter, param); } private void CommonLeftRight(string propertyName, object value) { common_left = Expression.Property(param, typeof(T).GetProperty(propertyName));//构建构建c.{propertyName}的结构 var member = Expression.Property(param, propertyName); var propertyType = ((PropertyInfo)member.Member).PropertyType; var converter = TypeDescriptor.GetConverter(propertyType); // 1 if (!converter.CanConvertFrom(typeof(string))) // 2 throw new NotSupportedException(); var propertyValue = converter.ConvertFromInvariantString(value.ToString()); // 3 var constant = Expression.Constant(propertyValue); common_right = Expression.Convert(constant, propertyType); // 4 // common_right = Expression.Constant(value, value.GetType());//构建一个常量,值是 value } /// <summary> /// 等于 = /// </summary> /// <param name="propertyName"></param> /// <param name="value"></param> public void Equal(string propertyName, object value) { this.CommonLeftRight(propertyName, value); Expression result = Expression.Equal(common_left, common_right); filter = Expression.And(filter, result); } /// <summary> /// 不等于 <> /// </summary> /// <param name="propertyName"></param> /// <param name="value"></param> public void NotEqual(string propertyName, object value) { this.CommonLeftRight(propertyName, value); Expression result = Expression.NotEqual(common_left, common_right); filter = Expression.And(filter, result); } /// <summary> /// 大于 > /// </summary> /// <param name="propertyName"></param> /// <param name="value"></param> public void GreaterThan(string propertyName, object value) { this.CommonLeftRight(propertyName, value); Expression result = Expression.GreaterThan(common_left, common_right); filter = Expression.And(filter, result); } /// <summary> /// 大于等于 >= /// </summary> /// <param name="propertyName"></param> /// <param name="value"></param> public void GreaterThanOrEqual(string propertyName, object value) { this.CommonLeftRight(propertyName, value); Expression result = Expression.GreaterThanOrEqual(common_left, common_right); filter = Expression.And(filter, result); } /// <summary> /// 小于 < /// </summary> /// <param name="propertyName"></param> /// <param name="value"></param> public void LessThan(string propertyName, object value) { this.CommonLeftRight(propertyName, value); Expression result = Expression.LessThan(common_left, common_right); filter = Expression.And(filter, result); } /// <summary> /// 小于等于 <= /// </summary> /// <param name="propertyName"></param> /// <param name="value"></param> public void LessThanOrEqual(string propertyName, object value) { this.CommonLeftRight(propertyName, value); Expression result = Expression.LessThanOrEqual(common_left, common_right); filter = Expression.And(filter, result); } /// <summary> /// 包含 LIKE /// </summary> /// <param name="propertyName"></param> /// <param name="value"></param> public void Contains(string propertyName, string value) { Expression left = Expression.Property(param, typeof(T).GetProperty(propertyName)); Expression right = Expression.Constant(value, value.GetType()); Expression result = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), right); filter = Expression.And(filter, result); } }
静态常量:
/// <summary> /// 系统通用的常量 /// </summary> public static class SystemCommonVar { /// <summary> /// 通用的查询字符串 /// </summary> public const string searchVal = "searchVal"; /// <summary> /// 高级查询的后缀名 /// </summary> public const string highSearchModeSuffix = "_FilterMode"; /// <summary> /// 通用的查询字符串的配置模式 /// </summary> public const string searchVal_FilterMode = searchVal + highSearchModeSuffix; /// <summary> /// 子搜索组件传递的属性名 /// </summary> public const string searchFormInputAttrs = "searchFormInputAttrs"; /// <summary> /// 通用的查询参数类的父类中的字段 /// </summary> public const string commnParamFatherPros = "page|pageSize|" + searchVal + "|" + searchVal_FilterMode + "|" + searchFormInputAttrs; }
类帮助:
/// <summary> /// 类帮助 /// </summary> public class ClassHelper { /// <summary> /// 获取属性对象列表 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> /// <param name="proName"></param> /// <param name="errMsg"></param> /// <returns></returns> public static PropertyInfo[] GetPropertyInfoList<T>(T t) { var pros = typeof(T).GetProperties(); return pros; } /// <summary> /// 获取属性对象 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> /// <param name="proName"></param> /// <param name="errMsg"></param> /// <returns></returns> public static PropertyInfo GetPropertyInfo<T>(T t, string proName, out string errMsg) { errMsg = ""; var pro = typeof(T).GetProperty(proName); if (pro == null) { errMsg = "属性名'" + proName + "'不存在类'" + typeof(T).Name + "'中"; return null; } return pro; } /// <summary> /// 获取属性的值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> /// <param name="proName"></param> /// <param name="errMsg"></param> /// <returns></returns> public static string GetPropertyValue<T>(T t, string proName, out string errMsg) { var pro = GetPropertyInfo<T>(t, proName, out errMsg); if (!string.IsNullOrEmpty(errMsg)) { return string.Empty; } var pro_value = pro.GetValue(t, null); var str = pro_value == null ? string.Empty : Convert.ToString(pro_value); return str; } /// <summary> /// 通过属性对象获取属性的值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> /// <param name="pro"></param> /// <returns></returns> public static string GetPropertyValueByObject<T>(T t, PropertyInfo pro) { var pro_value = pro.GetValue(t, null); var str = pro_value == null ? string.Empty : Convert.ToString(pro_value); return str; } /// <summary> /// 通过属性对象获取属性的值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> /// <param name="pro"></param> /// <returns></returns> public static object GetPropertyValue<T>(T t, PropertyInfo pro) { var pro_value = pro.GetValue(t, null); return pro_value; } /// <summary> /// 通过特性名获取特性 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> /// <param name="pro"></param> /// <returns></returns> public static object[] GetDIYAttributesByPro(PropertyInfo pro) { object[] Attributes = pro.GetCustomAttributes(typeof(HighSearchRangeAttribute), false); return Attributes; } /// <summary> /// 获取属性的值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> /// <param name="proName"></param> /// <param name="errMsg"></param> /// <returns></returns> public static List<string> GetPropertyValueForList<T>(T t, string proName, out string errMsg) { var pro = GetPropertyInfo<T>(t, proName, out errMsg); if (!string.IsNullOrEmpty(errMsg)) { return null; } var pro_value = pro.GetValue(t, null); var list = pro_value == null ? null : (List<string>)pro_value; return list; } }
4、使用方法
public class RoleParam : PageParam { /// <summary> /// 角色名 /// </summary> public string Name { get; set; } /// <summary> ///描述 /// </summary> public string Description { get; set; } #region 单独处理日期 //表示是 高级查询范围查询特性 [HighSearchRangeAttribute] public List<string> CreateTime { get; set; } #endregion #region 查询过滤模式 /// <summary> /// 角色名-查询过滤模式 /// </summary> public string Name_FilterMode { get; set; } /// <summary> ///描述-查询过滤模式 /// </summary> public string Description_FilterMode { get; set; } #endregion }
public class PageParam { /// <summary> /// 第几页 /// </summary> public int page { get; set; } /// <summary> /// 每页显示多少条 /// </summary> public int pageSize { get; set; } /// <summary> /// 关键字模糊查询 /// </summary> public string searchVal { get; set; } /// <summary> /// 通用查询配置 (1:模糊查询 , 2:精准查询) /// </summary> public int searchVal_FilterMode { get; set; } /// <summary> /// 子搜索组件传递的通用查询配置的属性名 /// </summary> public List<string> searchFormInputAttrs { get; set; } }
/// <summary> /// 方法返回实体类 /// </summary> public class FunReturnResultModel<T> { /// <summary> /// 执行是否成功 /// </summary> public bool IsSuccess { get; set; } /// <summary> /// 错误信息 /// </summary> public string ErrMsg { get; set; } /// <summary> /// 返回数据 /// </summary> public T data { get; set; } }
使用:
var whereConditions=WhereConditionsExtensions.GetWhereConditions<Role, RoleParam>(param); if (!whereConditions.IsSuccess) { return new MessageModel<PageModel<Role>>() { msg = whereConditions.ErrMsg, success = false, response = null }; }
whereConditions.data 就是生成的动态查询树。
前端请求参数示例:
人一定要靠自己