来源: ORM开发之解析lambda实现完整查询(附测试例子) – hubro – 博客园
上次讲解了怎么解析匿名对象(ORM开发之解析lambda实现group查询),这次来实现解析二元运算,完成基本条件语法
先看一个表达式
1
|
query.Where(b => b.Number == 10&&b.Id<20); |
表达式结构
一个运算符表示一个表达式,因此,此表达式实际上包含两个子表达式 b.Number==10 和b.Id<20 他们的关系为And
看一个子表达式 b.Number==10
按运算符为位置,左边为左操作数,右边为右操作数
以And操作符来看,b.Number==10也为左操作数,b.Id<20为右操作数
再增加其它条件时,也是同样的道理
那么我们解析将一个子表达式 b.Number==10 转换为SQL逻辑,则需要:
- 取出左表达式对应的字段名称 Number
- 取出运算符 =
- 取出右表达式的值 10
表达式类型
由上可以看出,表达式分左边和右边,左右两边也可是子表达式,它们形成一个表达式树,基类型都为System.Linq.Expressions.Expression
具体类型大致按下面划分为:
- BinaryExpression 表示包含二元运算符的表达式。 可以理解为一个子表达式,如 b.Number>10
- MemberExpression 表示访问字段或属性。 如 b.Number
- NewArrayExpression 表示创建新数组并可能初始化该新数组的元素。
- MethodCallExpression 表示对静态方法或实例方法的调用 如 b.Name.Contains(“123”)
- ConstantExpression 表示具有常量值的表达式 如 b.Name=”hubro”
- UnaryExpression 表示包含一元运算符的表达式
因此,需要根据不同的类型解析不同的表达式
开始解析
拆分表达式树
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
/// <summary> /// 拆分表达式树 /// </summary> /// <param name="left"></param> /// <param name="right"></param> /// <param name="type"></param> /// <returns></returns> public string BinaryExpressionHandler(Expression left, Expression right, ExpressionType type) { StringBuilder sb = new StringBuilder(); sb.Append( "(" ); string needParKey = "=,>,<,>=,<=,<>" ; string leftPar = RouteExpressionHandler(left); //获取左边 string typeStr = ExpressionTypeCast(type); //转换运算符 var isRight = needParKey.IndexOf(typeStr) > -1; //用以区分是解析左边的名称还是右边的值 string rightPar = RouteExpressionHandler(right, isRight); //获取右边 string appendLeft = leftPar; sb.Append(appendLeft); //字段名称 if (rightPar.ToUpper() == "NULL" ) { if (typeStr == "=" ) rightPar = " IS NULL " ; else if (typeStr == "<>" ) rightPar = " IS NOT NULL " ; } else { sb.Append(typeStr); } sb.Append(rightPar); sb.Append( ")" ); return sb.ToString(); } |
解析表达式
表达式树也会在这里处理,形成递归调用,当表达式是MemberExpression时,为了区分是左边的属性名还是右边的属性值,加了isRight进行区分
当是MethodCallExpression时,如果是左边,则需要进行解析(这里没有实现),右边只需要执行方法结果即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
/// <summary> /// 解析表达式 /// </summary> /// <param name="exp"></param> /// <param name="isRight"></param> /// <returns></returns> public string RouteExpressionHandler(Expression exp, bool isRight = false ) { if (exp is BinaryExpression) { BinaryExpression be = (BinaryExpression)exp; //重新拆分树,形成递归 return BinaryExpressionHandler(be.Left, be.Right, be.NodeType); } else if (exp is MemberExpression) { MemberExpression mExp = (MemberExpression)exp; if (isRight) //按表达式右边值 { var obj = Expression.Lambda(mExp).Compile().DynamicInvoke(); if (obj is Enum) { obj = ( int )obj; } return obj + "" ; } return mExp.Member.Name; //按左边的名称 } else if (exp is NewArrayExpression) { #region 数组 NewArrayExpression naExp = (NewArrayExpression)exp; StringBuilder sb = new StringBuilder(); foreach (Expression expression in naExp.Expressions) { sb.AppendFormat( ",{0}" , RouteExpressionHandler(expression)); } return sb.Length == 0 ? "" : sb.Remove(0, 1).ToString(); #endregion } else if (exp is MethodCallExpression) { if (isRight) { return Expression.Lambda(exp).Compile().DynamicInvoke() + "" ; } //在这里解析方法 throw new Exception( "暂不支持" ); } else if (exp is ConstantExpression) { #region 常量 ConstantExpression cExp = (ConstantExpression)exp; if (cExp.Value == null ) return "null" ; else { return cExp.Value.ToString(); } #endregion } else if (exp is UnaryExpression) { UnaryExpression ue = ((UnaryExpression)exp); return RouteExpressionHandler(ue.Operand, isRight); } return null ; } |
转换运算符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
public string ExpressionTypeCast(ExpressionType expType) { switch (expType) { case ExpressionType.And: return "&" ; case ExpressionType.AndAlso: return " AND " ; case ExpressionType.Equal: return "=" ; case ExpressionType.GreaterThan: return ">" ; case ExpressionType.GreaterThanOrEqual: return ">=" ; case ExpressionType.LessThan: return "<" ; case ExpressionType.LessThanOrEqual: return "<=" ; case ExpressionType.NotEqual: return "<>" ; case ExpressionType.Or: return "|" ; case ExpressionType.OrElse: return " OR " ; case ExpressionType.Add: case ExpressionType.AddChecked: return "+" ; case ExpressionType.Subtract: case ExpressionType.SubtractChecked: return "-" ; case ExpressionType.Divide: return "/" ; case ExpressionType.Multiply: case ExpressionType.MultiplyChecked: return "*" ; default : throw new InvalidCastException( "不支持的运算符" ); } } |
获取解析值
1
2
3
4
5
6
7
8
9
|
internal string FormatExpression(Expression<Func<T, bool >> expression) { string condition; var visitor = new ExpressionVisitor(); if (expression == null ) return "" ; condition = visitor.RouteExpressionHandler(expression.Body); return condition; } |
拼接完整的SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public string GetQuery() { string where = Condition; where = string .IsNullOrEmpty( where ) ? " 1=1 " : where ; #region group判断 if (groupFields.Count > 0) { where += " group by " ; foreach ( var item in groupFields) { where += item + "," ; } where = where .Substring(0, where .Length - 1); } #endregion string tableName = typeof (T).Name; string fileds = string .Join( "," , queryFields); var part = string .Format( "select {0} from {1} where {2}" , fileds, tableName, where ); return part; } |
运行输出
1
2
3
4
5
6
7
8
9
|
var query = new LambdaQuery<Product>(); query.Select(b => new { b.BarCode, b.ProductName, total = b.BarCode.COUNT() }); query.GroupBy(b => new { b.BarCode, b.ProductName }); query.Where(b => b.ProductName == "ddd" ); query.Where(b => b.Number == 10); query.Where(b => b.Number == new aa().bb); //测试获取对象参数 query.OrderBy(b => b.BarCode.COUNT(), true ); Console.Write(query.GetQuery()); |
这样,一般查询就能用lambda来表示了,但是一些SQL函数,是没法表示的,和之前说的一样,可以用扩展方法解决
上面上解析方法调用表达式里,解析即可,解析方法比较复杂,就不在这里写了
1
2
3
4
5
6
7
8
9
|
else if (exp is MethodCallExpression) { if (isRight) { return Expression.Lambda(exp).Compile().DynamicInvoke() + "" ; } //在这里解析方法 throw new Exception( "暂不支持" ); } |
测试例子下载 http://files.cnblogs.com/files/hubro/LambdaQueryTest2.rar