zoukankan      html  css  js  c++  java
  • ORM开发之解析lambda实现完整查询(附测试例子)

    上次讲解了怎么解析匿名对象(ORM开发之解析lambda实现group查询),这次来实现解析二元运算,完成基本条件语法

    先看一个表达式

    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 表示包含一元运算符的表达式

    因此,需要根据不同的类型解析不同的表达式

    开始解析

    拆分表达式树

    /// <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时,如果是左边,则需要进行解析(这里没有实现),右边只需要执行方法结果即可

    /// <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;
            }
    

    转换运算符

    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("不支持的运算符");
                }
            }
    

    获取解析值

    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

    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;
            }
    

    运行输出

    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函数,是没法表示的,和之前说的一样,可以用扩展方法解决

    上面上解析方法调用表达式里,解析即可,解析方法比较复杂,就不在这里写了

    else if (exp is MethodCallExpression)
                {
                    if (isRight)
                    {
                        return Expression.Lambda(exp).Compile().DynamicInvoke() + "";
                    }
                    //在这里解析方法
                    throw new Exception("暂不支持");
                }
    

    测试例子下载 http://files.cnblogs.com/files/hubro/LambdaQueryTest2.rar

  • 相关阅读:
    2018-9-28-WPF-自定义-TextBoxView-的-Margin-大小
    2018-6-24-WPF-使用RPC调用其他进程
    2018-6-24-WPF-使用RPC调用其他进程
    2019-9-2-通过命令行使用微信
    2019-9-2-通过命令行使用微信
    2019-9-2-git-需要知道的1000个问题
    2019-9-2-git-需要知道的1000个问题
    pip install 安装出现问题:UnicodeEncodeError: 'ascii' codec can't encode characters in position XX的解决办法
    Java 使用Redis缓存工具的图文详细方法
    0基础学编程,我想给你这 5 个建议
  • 原文地址:https://www.cnblogs.com/hubro/p/4381337.html
Copyright © 2011-2022 走看看