上次講解了怎么解析匿名對象(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邏輯,則需要:
- 取出左表達式對應的字段名稱 Number
- 取出運算符 =
- 取出右表達式的值 10
表達式類型
由上可以看出,表達式分左邊和右邊,左右兩邊也可是子表達式,它們形成一個表達式樹,基類型都為System.Linq.Expressions.Expression
具體類型大致按下面划分為:
- BinaryExpression 表示包含二元運算符的表達式。 可以理解為一個子表達式,如 b.Number>10
- MemberExpression 表示訪問字段或屬性。 如 b.Number
- NewArrayExpression 表示創建新數組並可能初始化該新數組的元素。
- MethodCallExpression 表示對靜態方法或實例方法的調用 如 b.Name.Contains("123")
- ConstantExpression 表示具有常量值的表達式 如 b.Name="hubro"
- 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