根据表达式树动态生成Lambda表达式

时间:2022-06-26 20:27:13

1.准备

  1. 环境:Asp.Net MVC5 、EF6
  2. 前置知识:反射、使用过EF编写过Lambda表达式

2.基础类库

2.1该高级条件的类型

 1     /// <summary>
 2     /// 当前条件所属类型
 3     /// </summary>
 4     public enum Em_AS_ConditionType
 5     {
 6         /// <summary>
 7         /// 8         /// </summary>
 9         [Description("")]
10         And = 0,
11 
12         /// <summary>
13         ///14         /// </summary>
15         [Description("")]
16         Or = 1
17     }

2.2搜索条件

 1     /// <summary>
 2     /// 高级搜索条件
 3     /// </summary>
 4     public enum Em_AS_Condition
 5     {
 6         /// <summary>
 7         /// 包含
 8         /// </summary>
 9         [Description("包含")]
10         Include = 0,
11 
12         /// <summary>
13         /// 等于
14         /// </summary>
15         [Description("=")]
16         Equal = 1,
17 
18         /// <summary>
19         /// 大于等于
20         /// </summary>
21         [Description(">=")]
22         GtEqual = 2,
23 
24         /// <summary>
25         /// 大于
26         /// </summary>
27         [Description(">")]
28         Gt = 3,
29 
30         /// <summary>
31         /// 小于等于
32         /// </summary>
33         [Description("<=")]
34         LtEqual = 4,
35 
36         /// <summary>
37         /// 小于
38         /// </summary>
39         [Description("<")]
40         Lt = 5,
41     }

2.3前端传来的数据模型格式

    /// <summary>
    /// 高级查询
    /// </summary>public class QM_Adv
    {
        /// <summary>
        /// 属性名称
        /// </summary>public string PropName { get; set; }

        /// <summary>
        /// 条件
        /// </summary>public Em_AS_Condition Condition { get; set; }

        /// <summary>
        /// 关键字
        /// </summary>public string Keyword { get; set; }

        /// <summary>
        /// 这组条件与其它条件的关系
        /// </summary>public Em_AS_ConditionType ConditionType { get; set; }
    }

3.准备好基础类库,然后就可以编写表达式树生成Lambda的代码了

3.1.表达式的种类

  1. BinaryExpression
  2. MethodCallExpression
3.1.1:BinaryExpression: 生成的lambda如:
p=>p.Age==21
p=>p.Age<=21
p=>p.Age!=21
 3.1.2:MethodCallExpression:生成的lambda如
p=>p.Name.Contains("陈")

 3.2方法介绍

3.2.1:Expression.Constant(object value) 创建一个常数表达式,一般方在表达式的右边;也可同时放在表达式的两边组成一个恒为真、恒为假的表达式

3.2.2:Expression.Property(ParameterExpression pe, string propertyName) 访问字段或属性,一般放在表达式左边;生成的表示式就是上面中的p=>p.Age,p=>p.Name

3.2.3:Expression.Call(Expression instance,MethodInfo method,Expression[] arguments) 返回一个MethodCallExpression

3.2.4:Expression.Equal(Expression left, Expression right) 返回两边相等的BinaryExpression如:p=>p.Age==21
3.2.5:还有很多类似与Expression.Equal这个方法的,可查阅https://msdn.microsoft.com/zh-cn/library/system.linq.expressions.expression(v=vs.110).aspx

3.3 示例代码

3.3.1:类库

  1         /// <summary>
  2         /// 恒为真
  3         /// </summary>
  4         /// <returns>表达式</returns>
  5         public static Expression EqualTrueCompare()
  6         {
  7             var type = typeof(string);
  8             var pRef = Expression.Constant(true);
  9             var constantReference = Expression.Constant(true);
 10             var be = Expression.Equal(pRef, constantReference);
 11             return be;
 12         }
 13 
 14         /// <summary>
 15         /// 包含
 16         /// </summary>
 17         /// <typeparam name="TSource">数据类型</typeparam>
 18         /// <param name="pe">左侧表达式参数</param>
 19         /// <param name="property">属性</param>
 20         /// <param name="value"></param>
 21         /// <returns>表达式</returns>
 22         public static Expression IncludeCompare<TSource>(ParameterExpression pe, string property, object value)
 23         {
 24             try
 25             {
 26                 var type = typeof(TSource);
 27                 object val = null;
 28                 Type dyType = TypeConvert.GetValue(type, property, value, out val);
 29                 if (val == null) return null;
 30                 var propertyReference = Expression.Property(pe, property);
 31                 var constantReference = Expression.Constant(val, dyType);
 32                 var be = Expression.Call(propertyReference, typeof(string).GetMethod("Contains"), constantReference);
 33                 return be;
 34             }
 35             catch (Exception e)
 36             {
 37                 LogHelper.WriteLog("表达式树生成错误,此条件将被忽略=>" + e.Message, e);
 38                 return null;
 39             }
 40         }
 41 
 42         /// <summary>
 43         /// 等于
 44         /// </summary>
 45         /// <typeparam name="TSource">数据类型</typeparam>
 46         /// <param name="pe">左侧表达式参数</param>
 47         /// <param name="property">属性</param>
 48         /// <param name="value"></param>
 49         /// <returns>表达式</returns>
 50         public static Expression EqualCompare<TSource>(ParameterExpression pe, string property, object value)
 51         {
 52             try
 53             {
 54                 var type = typeof(TSource);
 55                 object val = null;
 56                 Type dyType = TypeConvert.GetValue(type, property, value, out val);
 57                 if (val == null) return null;
 58                 var propertyReference = Expression.Property(pe, property);
 59                 var constantReference = Expression.Constant(val, dyType);
 60                 BinaryExpression be = Expression.Equal(propertyReference, constantReference);
 61                 return be;
 62             }
 63             catch (Exception e)
 64             {
 65                 LogHelper.WriteLog("表达式树生成错误,此条件将被忽略=>" + e.Message, e);
 66                 return null;
 67             }
 68         }
 69 
 70         /// <summary>
 71         /// 大于等于
 72         /// </summary>
 73         /// <typeparam name="TSource">数据类型</typeparam>
 74         /// <param name="pe">左侧表达式参数</param>
 75         /// <param name="property">属性</param>
 76         /// <param name="value"></param>
 77         /// <returns>表达式</returns>
 78         public static Expression GtEqualCompare<TSource>(ParameterExpression pe, string property, object value)
 79         {
 80             try
 81             {
 82                 var type = typeof(TSource);
 83                 object val = null;
 84                 Type dyType = TypeConvert.GetValue(type, property, value, out val);
 85                 if (val == null) return null;
 86                 var propertyReference = Expression.Property(pe, property);
 87                 var constantReference = Expression.Constant(val, dyType);
 88                 BinaryExpression be = Expression.GreaterThanOrEqual(propertyReference, constantReference);
 89                 return be;
 90             }
 91             catch (Exception e)
 92             {
 93                 LogHelper.WriteLog("表达式树生成错误,此条件将被忽略=>" + e.Message, e);
 94                 return null;
 95             }
 96         }
 97 
 98         /// <summary>
 99         /// 大于
100         /// </summary>
101         /// <typeparam name="TSource">数据类型</typeparam>
102         /// <param name="pe">左侧表达式参数</param>
103         /// <param name="property">属性</param>
104         /// <param name="value"></param>
105         /// <returns>表达式</returns>
106         public static Expression GtCompare<TSource>(ParameterExpression pe, string property, object value)
107         {
108             try
109             {
110                 var type = typeof(TSource);
111                 object val = null;
112                 Type dyType = TypeConvert.GetValue(type, property, value, out val);
113                 if (val == null) return null;
114                 var propertyReference = Expression.Property(pe, property);
115                 var constantReference = Expression.Constant(val, dyType);
116                 BinaryExpression be = Expression.GreaterThan(propertyReference, constantReference);
117                 return be;
118             }
119             catch (Exception e)
120             {
121                 LogHelper.WriteLog("表达式树生成错误,此条件将被忽略=>" + e.Message, e);
122                 return null;
123             }
124         }
125 
126         /// <summary>
127         /// 小于等于
128         /// </summary>
129         /// <typeparam name="TSource">数据类型</typeparam>
130         /// <param name="pe">左侧表达式参数</param>
131         /// <param name="property">属性</param>
132         /// <param name="value"></param>
133         /// <returns>表达式</returns>
134         public static Expression LtEqualCompare<TSource>(ParameterExpression pe, string property, object value)
135         {
136             try
137             {
138                 var type = typeof(TSource);
139                 object val = null;
140                 Type dyType = TypeConvert.GetValue(type, property, value, out val);
141                 if (val == null) return null;
142                 var propertyReference = Expression.Property(pe, property);
143                 var constantReference = Expression.Constant(val, dyType);
144                 BinaryExpression be = Expression.LessThanOrEqual(propertyReference, constantReference);
145                 return be;
146             }
147             catch (Exception e)
148             {
149                 LogHelper.WriteLog("表达式树生成错误,此条件将被忽略=>" + e.Message, e);
150                 return null;
151             }
152         }
153 
154         /// <summary>
155         /// 小于
156         /// </summary>
157         /// <typeparam name="TSource">数据类型</typeparam>
158         /// <param name="pe">左侧表达式参数</param>
159         /// <param name="property">属性</param>
160         /// <param name="value"></param>
161         /// <returns>表达式</returns>
162         public static Expression LtCompare<TSource>(ParameterExpression pe, string property, object value)
163         {
164             try
165             {
166                 var type = typeof(TSource);
167                 object val = null;
168                 Type dyType = TypeConvert.GetValue(type, property, value, out val);
169                 if (val == null) return null;
170                 var propertyReference = Expression.Property(pe, property);
171                 var constantReference = Expression.Constant(val, dyType);
172                 BinaryExpression be = Expression.LessThan(propertyReference, constantReference);
173                 return be;
174             }
175             catch (Exception e)
176             {
177                 LogHelper.WriteLog("表达式树生成错误,此条件将被忽略=>" + e.Message, e);
178                 return null;
179             }
180         }

 

3.3.2:TypeConvert.cs

  1         /// <summary>
  2         /// 根据实体类动态转换值类型,并输出转换后的值
  3         /// </summary>
  4         /// <param name="type">实体类型</param>
  5         /// <param name="propName">属性名</param>
  6         /// <param name="value">原始值</param>
  7         /// <param name="oVal">输出值</param>
  8         /// <returns>该属性对应的数据类型</returns>
  9         public static Type GetValue(Type type, string propName, object value, out object oVal)
 10         {
 11             Type objType = null;
 12             object objValue = null;
 13             foreach (var pi in type.GetProperties())
 14             {
 15                 if (pi.Name == propName)
 16                 {
 17                     objType = pi.PropertyType;
 18                     string typeStr = string.Empty;
 19                     bool isNullType = IsNullableType(pi.PropertyType);
 20                     if (isNullType)
 21                     {
 22                         typeStr = pi.PropertyType.GetGenericArguments()[0].Name.ToLower();
 23                     }
 24                     else
 25                     {
 26                         typeStr = pi.PropertyType.Name.ToLower();
 27                     }
 28                     switch (typeStr)
 29                     {
 30                         case "string":
 31                             objValue = value + "";
 32                             break;
 33                         case "datetime":
 34                             DateTime tempDateTime;
 35                             bool isDateTime = DateTime.TryParse(value + "", out tempDateTime);
 36                             if (isDateTime)
 37                             {
 38                                 objValue = tempDateTime;
 39                             }
 40                             else
 41                             {
 42                                 objValue = null;
 43                             }
 44                             break;
 45                         case "int16":
 46                         case "int32":
 47                         case "int64":
 48                             int tempint = 0;
 49                             bool isInt = int.TryParse(value + "", out tempint);
 50                             if (isInt)
 51                             {
 52                                 objValue = tempint;
 53                             }
 54                             else
 55                             {
 56                                 objValue = null;
 57                             }
 58                             break;
 59                         case "double":
 60                             double tempDouble = 0;
 61                             var tempStrDouble = value + "";
 62                             bool isDouble = double.TryParse(tempStrDouble, out tempDouble);
 63                             if (isDouble)
 64                             {
 65                                 objValue = tempDouble;
 66                             }
 67                             else
 68                             {
 69                                 objValue = null;
 70                             }
 71                             break;
 72                         case "decimal":
 73                             decimal tempDecimal = 0;
 74                             var tempStr = value + "";
 75                             bool isdecimal = decimal.TryParse(tempStr, out tempDecimal);
 76                             if (isdecimal)
 77                             {
 78                                 objValue = tempDecimal;
 79                             }
 80                             else
 81                             {
 82                                 objValue = null;
 83                             }
 84                             break;
 85                         default:
 86 
 87                             break;
 88                     }
 89                 }
 90             }
 91             oVal = objValue;
 92             return objType;
 93         }
 94 
 95         /// <summary>
 96         /// 判定是否为可空类型
 97         /// </summary>
 98         /// <param name="theType">类型</param>
 99         /// <returns></returns>
100         public static bool IsNullableType(Type theType)
101         {
102             return (theType.IsGenericType && theType.GetGenericTypeDefinition() == typeof(Nullable<>));
103         }

3.4:将多个查询条件转换成表达式树

 1         /// <summary>
 2         /// 将高级查询转换成对应的表达式树
 3         /// </summary>
 4         /// <typeparam name="TSource">类型</typeparam>
 5         /// <param name="conditions">高级查询条件集合</param>
 6         /// <returns>对象数据类型的表达式树</returns>
 7         public static Expression<Func<TSource, bool>> ConvertToExpression<TSource>(this List<QM_Adv> conditions)
 8         {
 9             var type = typeof(TSource);
10             var pe = Expression.Parameter(type, "p");
11 
12             Expression exp = null;
13             if (conditions == null)
14             {
15                 exp = ExpressionLib.EqualTrueCompare();
16                 return Expression.Lambda<Func<TSource, bool>>(exp, pe);
17             }
18             //并:生成交集的条件
19             conditions.Where(p => p.ConditionType == Em_AS_ConditionType.And).ToList().ForEach(p =>
20             {
21                 Expression temp = GetExpressionTemp<TSource>(p, pe);
22                 if (exp == null)
23                 {
24                     if (temp != null)
25                     {
26                         exp = temp;
27                     }
28                 }
29                 else
30                 {
31                     if (temp != null)
32                     {
33                         exp = Expression.AndAlso(exp, temp);
34                     }
35                 }
36             });
37 
38             //或:生成并集的条件
39             conditions.Where(p => p.ConditionType == Em_AS_ConditionType.Or).ToList().ForEach(p =>
40             {
41                 Expression temp = GetExpressionTemp<TSource>(p, pe);
42                 if (exp == null)
43                 {
44                     if (temp != null)
45                     {
46                         exp = temp;
47                     }
48                 }
49                 else
50                 {
51                     if (temp != null)
52                     {
53                         exp = Expression.Or(exp, temp);
54                     }
55                 }
56             });
57             if (exp == null)
58             {
59                 exp = ExpressionLib.EqualTrueCompare();
60             }
61 
62             return Expression.Lambda<Func<TSource, bool>>(exp, pe);
63         }
64 
65         private static Expression GetExpressionTemp<TSource>(QM_Adv p, ParameterExpression pe)
66         {
67             Expression temp = null;
68             switch (p.Condition)
69             {
70                 case Em_AS_Condition.Include:
71                     temp = ExpressionLib.IncludeCompare<TSource>(pe, p.PropName, p.Keyword);
72                     break;
73                 case Em_AS_Condition.GtEqual:
74                     temp = ExpressionLib.GtEqualCompare<TSource>(pe, p.PropName, p.Keyword);
75                     break;
76                 case Em_AS_Condition.Gt:
77                     temp = ExpressionLib.GtCompare<TSource>(pe, p.PropName, p.Keyword);
78                     break;
79                 case Em_AS_Condition.LtEqual:
80 
81                     temp = ExpressionLib.LtEqualCompare<TSource>(pe, p.PropName, p.Keyword);
82                     break;
83                 case Em_AS_Condition.Lt:
84                     temp = ExpressionLib.LtCompare<TSource>(pe, p.PropName, p.Keyword);
85                     break;
86                 case Em_AS_Condition.Equal:
87                     temp = ExpressionLib.EqualCompare<TSource>(pe, p.PropName, p.Keyword);
88                     break;
89                 default:
90                     break;
91             }
92             return temp;
93         }

4.1最后结语

这是我第一次写博客,能力有限,有不对的地方欢迎指出