動態構建Expression表達式樹


話說.Net已經發展到4.5了,大家對Lambda和Linq應該比較熟悉了。比如我們要取出產品集合里面SKU以"123"開頭的產品集,就可以這么寫:
Products=Products.Where(p=>p.SKUCode.StartWith("123"));

現下有這么個需求,用戶輸入以逗號分隔的字符串,求取SKU以分隔的字符串開頭的產品集,如用戶輸入"123,234,456",那么就取出SKU以"123"開頭或以"234"開頭或以"456"開頭的產品集合。於是我計上心頭,欲以循環解之,無解。為什么呢?因為這里有個關鍵字“或”。假如是“並”需求,我們大可信手拈來,如下:

1 string[] starts = input.Split(','); 2 foreach (string start in starts) 3 { 4     Products = Products.Where(p => p.SKUCode.StartWith(start)); 5 }

對於“或”來說,Linq似乎並沒有提供一個明確的方法(比如這里的Where)供我們使用,我想是因為“或”的作用域比較難用程序語言界定,因此一旦使用“或”去聯接,那最終的結果可能並不是預期的。若“或”對應的方法是Or,那么循環體內的語句變為Products = Products.Or(p => p.SKUCode.StartWith(start));不知所雲。我們想要的其實應該是條件的拼接:
p => p.SKUCode.StartWith(start1) || p.SKUCode.StartWith(start2) || p.SKUCode.StartWith(start3) || ……

可惜Linq同樣沒有給我們提供動態拼接條件的方便的語法(何為方便,我的想法是同字符串拼接,拼接完畢后可簡單快捷地轉成可執行的語句。啥,你說Javascript?心里想想就是了,說出來干嘛),不過給了稍稍復雜點的方式。下面是代碼: 

 1 /// <summary>
 2 /// 根據條件數據動態生成或連接條件  3 /// </summary>
 4 /// <typeparam name="TSource">集合項類型</typeparam>
 5 /// <param name="sourcePropertyName">待比較的集合項屬性</param>
 6 /// <param name="methodName">方法名稱</param>
 7 /// <param name="objs">條件數據</param>
 8 /// <returns></returns>
 9 public static Expression<Func<TSource, bool>> GenerateOrElseConditionWithArray<TSource>(string sourcePropertyName, string methodName, IEnumerable<object> objs) 10 { 11     if (objs != null && objs.Count() > 0) 12  { 13         var len = objs.Count(); 14         var p = Expression.Parameter(typeof(TSource), "p"); 15         var propertyName = Expression.Property(p, sourcePropertyName); 16         var body = Expression.Equal(Expression.Call(propertyName, methodName, null, Expression.Constant(objs.First())), Expression.Constant(true)); 17         for (int i = 1; i < len; i++) 18  { 19             var pcode = objs.ElementAt(i); 20             body = Expression.OrElse(body, Expression.Call(propertyName, methodName, null, Expression.Constant(pcode))); 21  } 22         Expression<Func<TSource, bool>> orExp = Expression.Lambda<Func<TSource, bool>>(body, p); 23         return orExp; 24  } 25     return null; 26 }

 上述代碼是我較早時候寫的,此時看似乎有些不妥之處,最近較忙,也懶於潤色修改了,歡迎朋友指正。

現在我們就可以這么實現上述需求:

1 string[] starts = input.Split(','); 2 var codeExp = GenerateOrElseConditionWithArray<Product>("SKUCode", "StartsWith", starts); 3 if (codeExp != null) 4     Products = Products.Where(codeExp);

 

后記:老外寫了一個類,貌似比我的專業多了,Dynamically Composing Expression Predicates.

 

 轉載請注明本文出處:http://www.cnblogs.com/newton/archive/2012/12/17/2821159.html


免責聲明!

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



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