ORM开发之解析lambda实现完整查询(附测试例子) - hubro - 博客园

来源: 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逻辑,则需要:

  1. 取出左表达式对应的字段名称 Number
  2. 取出运算符 =
  3. 取出右表达式的值 10

表达式类型

由上可以看出,表达式分左边和右边,左右两边也可是子表达式,它们形成一个表达式树,基类型都为System.Linq.Expressions.Expression

具体类型大致按下面划分为:

  1. BinaryExpression 表示包含二元运算符的表达式。 可以理解为一个子表达式,如 b.Number>10
  2. MemberExpression 表示访问字段或属性。 如 b.Number
  3. NewArrayExpression 表示创建新数组并可能初始化该新数组的元素。
  4. MethodCallExpression 表示对静态方法或实例方法的调用 如 b.Name.Contains(“123”)
  5. ConstantExpression 表示具有常量值的表达式 如 b.Name=”hubro”
  6. 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

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏