話說.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