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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM