寫在前面
系列文章
表達式樹解析
表達式樹特性
編譯表達樹
總結
讓我們首先簡單回顧一下上篇文章介紹的內(nèi)容,上篇文章介紹了表達式樹的基本概念(表達式樹又稱為“表達式目錄樹”,以數(shù)據(jù)形式表示語言級代碼,它是一種抽象語法樹或者說是一種數(shù)據(jù)結構),以及兩種創(chuàng)建表達式樹目錄樹的方式:以lambda表達式的方式創(chuàng)建,通過API靜態(tài)方法創(chuàng)建。由于不能將有語句體的lambda表達式轉換為表達式樹,而有時我們又有這樣的需求,那么這種情況你可以選擇API的靜態(tài)方法方式創(chuàng)建,在 .NET Framework 4 中,API 表達式樹還支持賦值表達式和控制流表達式,比如循環(huán)、條件塊和 try-catch 塊等。
Linq之Lambda表達式初步認識
Linq之Lambda進階
Linq之隱式類型、自動屬性、初始化器、匿名類
Linq之擴展方法
Linq之Expression初見
我們可以通過API方式創(chuàng)建表達式樹,那么我們有沒有辦法,將給定的表達式樹進行解析,分別得到各個部分呢?答案是肯定,下面看一個例子。
有一個這樣的表達式樹
1 //創(chuàng)建表達式樹2 Expression<Func<int, bool>> expTree = num => num >= 5;
可以這樣來解析,分別得到各個部分
1 //創(chuàng)建表達式樹 2 Expression<Func<int, bool>> expTree = num => num >= 5; 3 //獲取輸入?yún)?shù) 4 ParameterExpression param = expTree.Parameters[0]; 5 //獲取lambda表達式主題部分 6 BinaryExpression body = (BinaryExpression)expTree.Body; 7 //獲取num>=5的右半部分 8 ConstantExpression right = (ConstantExpression)body.Right; 9 //獲取num>=5的左半部分10 ParameterExpression left = (ParameterExpression)body.Left;11 //獲取比較運算符12 ExpressionType type = body.NodeType;13 Console.WriteLine("解析后:{0} {1} {2}",left,type,right);輸出結果

是不是很爽?不知道到這里,你是否對ORM框架中,lambda表達式是如何轉化為sql語句有那么一點點的靈感?沒有沒關系,咱們繼續(xù)看一個例子。如果數(shù)據(jù)庫中有Person這樣的一個數(shù)據(jù)表。咱們項目中有對應的Person這樣的一個持久化類。那么我們創(chuàng)建一個這樣的一個查詢方法,返回所有齡大于等于18歲的成年人的sql語句。
1 namespace Wolfy.ORMDemo 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 string sql = Query<Person>(person => person.Age >= 18); 8 Console.WriteLine(sql); 9 Console.Read();10 }11 /// <summary>12 /// 得到查詢的sql語句13 /// </summary>14 /// <param name="epression">篩選條件</param>15 /// <returns></returns>16 static string Query<T>(Expression<Func<T, bool>> epression) where T : class,new()17 {18 //獲取輸入?yún)?shù)19 ParameterExpression param = epression.Parameters[0];20 //獲取lambda表達式主體部分21 BinaryExpression body = (BinaryExpression)epression.Body;22 //解析 person.Age23 Expression left = body.Left;24 string name = (left as MemberExpression).Member.Name;25 //獲取主體的右部分26 ConstantExpression right = (ConstantExpression)body.Right;27 //獲取運算符28 ExpressionType nodeType = body.NodeType;29 StringBuilder sb = new StringBuilder();30 //使用反射獲取實體所有屬性,拼接在sql語句中31 Type type = typeof(T);32 PropertyInfo[] properties = type.GetProperties();33 sb.Append("select ");34 for (int i = 0; i < properties.Length; i++)35 {36 PropertyInfo property = properties[i];37 if (i == properties.Length - 1)38 {39 sb.Append(property.Name + " ");40 }41 else42 {43 sb.Append(property.Name + " ,");44 }45 }46 sb.Append("from ");47 sb.Append(type.Name);48 sb.Append(" where ");49 sb.Append(name);50 if (nodeType == ExpressionType.GreaterThanOrEqual)51 {52 sb.Append(">=");53 }54 sb.Append(right);55 return sb.ToString();56 }57 }58 class Person59 {60 public int Age { set; get; }61 public string Name { set; get; }62 }63 }輸出結果

是不是很方便?傳進來一個lambda表達式,就可以通過orm框架內(nèi)部解析,然后轉化為sql語句。也就是通過編寫lambda就等于寫了sql語句,也不用擔心不會寫sql語句了。
表達式樹應具有永久性。這意味著如果你想修改某個表達式樹,則必須復制該表達式樹然后替換其中的節(jié)點來創(chuàng)建一個新的表達式樹。
那如何修改呢?
可以通過ExpressionVisitor類遍歷現(xiàn)有表達式樹,并復制它訪問的每個節(jié)點。

一個例子
在項目中添加一個AndAlsoModifier 類。
將表達式樹中的AndAlse修改為OrElse,代碼如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Linq.Expressions; 7 namespace Wolfy.ExpressionModifyDemo 8 { 9 /*該類繼承 ExpressionVisitor 類,并且專用于修改表示條件 AND 運算的表達式。 10 * 它將這些運算從條件 AND 更改為條件 OR。 11 * 為此,該類將重寫基類型的 VisitBinary 方法,這是因為條件 AND 表達式表示為二元表達式。12 * 在 VisitBinary 方法中,如果傳遞到該方法的表達式表示條件 AND 運算,13 * 代碼將構造一個包含條件 OR 運算符(而不是條件 AND 運算符)的新表達式。 14 * 如果傳遞到 VisitBinary 的表達式不表示條件 AND 運算,則該方法交由基類實現(xiàn)來處理。15 * 基類方法構造類似于傳入的表達式樹的節(jié)點,但這些節(jié)點將其子目錄樹替換為訪問器遞歸生成的表達式樹。*/16 public class AndAlsoModifier : ExpressionVisitor17 {18 public Expression Modify(Expression expression)19 {20 return Visit(expression);21 }22 protected override Expression VisitBinary(BinaryExpression node)23 {24 if (node.NodeType == ExpressionType.AndAlso)25 {26 Expression left = this.Visit(node.Left);27 Expression right = this.Visit(node.Right);28 //修改AndAlse為OrElse29 return Expression.MakeBinary(ExpressionType.OrElse, left, right, node.IsLiftedToNull, node.Method);30 }31 return base.VisitBinary(node);32 }33 }34 }測試代碼
1 namespace Wolfy.ExpressionModifyDemo 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 Expression<Func<string, bool>> expr = name => name.Length > 10 && name.StartsWith("G"); 8 //修改前 9 Console.WriteLine(expr);10 AndAlsoModifier treeModifier = new AndAlsoModifier();11 Expression modifiedExpr = treeModifier.Modify((Expression)expr);12 //修改后13 Console.WriteLine(modifiedExpr);14 Console.Read();15 }16 }17 }輸出結果

小結:修改表達式樹,需繼承ExpressionVisitor類,并重寫它的VisitBinary(如果是類似AND這類的二元表達式)方法。再舉一個例子,如果要將大于修改為小于等于,可修改VisitBinary方法的實現(xiàn)。
1 protected override Expression VisitBinary(BinaryExpression node) 2 { 3 if (node.NodeType == ExpressionType.GreaterThan) 4 { 5 Expression left = this.Visit(node.Left); 6 Expression right = this.Visit(node.Right); 7 //修改> 為<= 8 return Expression.MakeBinary(ExpressionType.LessThanOrEqual, left, right, node.IsLiftedToNull, node.Method); 9 }10 return base.VisitBinary(node);11 }結果

新聞熱點
疑難解答