2017-09-24 128 views
0

相關:Create a Lambda Expression With 3 conditions轉換包含表達式樹

請考慮下面的代碼:

from a in myTbl 
where a.Address.Contains(strToCheck) 
select a 

我如何將此轉換爲表達式樹和上面寫有表達式代碼? 主要問題是將a.Address.Contains(strToCheck)轉換爲Expression Tree

編輯1)地址是string場和strToCheckstring

感謝

+0

是什麼轉換之間的區別' 。選擇()'到表達式樹和'.Contains()'到表達式樹?沒有。所以如果你知道如何轉換'Select',你應該知道如何轉換'Contains'。 –

+0

謝謝。但我對錶達樹並不陌生,因此沒有多少資源。如果你知道答案,請幫我 – Arian

+0

SO包含很多例子,如何編寫調用某種方法的表達式樹,例如'Any'' Where'等等。你可以查看幾個例子:[第一](https://stackoverflow.com/questions/45887790/access-childrenlist-related-property-in-expression-tree)和[second](https://stackoverflow.com/問題/ 45765350 /創建謂上帶有一個含binaryexpression- - 多參數)。主要思想是你從相應的'MethodInfo'創建'MethodCallExpression'。順便說一下,爲什麼你想將你的例子轉換成'ExpressionTree'? –

回答

2

a.Address.Contains(strToCheck)代表在呼叫string.Contains實例方法a.Address例如strToCheck說法

建立相應的表達,最簡單的方法是使用下面的Expression.Call overload

public static MethodCallExpression Call(
    Expression instance, 
    string methodName, 
    Type[] typeArguments, 
    params Expression[] arguments 
) 

像這樣(使用鏈接的問題條款):

var body = Expression.Call(
    Expression.PropertyOrField(param, "Address"), // instance 
    "Contains", // method 
    Type.EmptyTypes, // no generic type arguments 
    Expression.Constant(strToCheck) // argument 
); 
+0

這是非常好的。謝謝 – Arian

0

沒有指定MYTBL的類型,
所以我創建了只使用一個簡單的解決方案對象列表。

using System; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Collections.Generic; 

namespace Test 
{ 
    class Program 
    { 
     static void Main(string[] args) { 
      var adresses = FilterByAddress("Address", new List<Person> { new Person { Address = "Address1" }, new Person { Address = "AAAAAA" } }); 
     } 

     public static IEnumerable<Person> FilterByAddress(string strToCheck, List<Person> list) { 
      var listParam = Expression.Parameter(typeof(IEnumerable<Person>), "list"); 
      Expression<Func<Person, bool>> contains = a => a.Address.Contains(strToCheck); 
      var select = typeof(Enumerable).GetMethods().Single(m => m.Name.Equals("Where") && m.GetParameters()[1].ParameterType.GetGenericArguments().Length == 2); 
      var genericMethod = select.MakeGenericMethod(new[] { typeof(Person) }); 
      var call = Expression.Call(null, genericMethod, new Expression[] { listParam, contains }); 
      var lambda = Expression.Lambda<Func<IEnumerable<Person>, IEnumerable<Person>>>(call, new[] { listParam }); 

      return lambda.Compile().Invoke(list); 
     } 
    } 

    public class Person 
    { 
     public string Address { get; set; } 
    } 
} 

如果要通過謂詞使用過濾器可以傳遞一個Expresssion<Func<Person, bool>>作爲參數(一行)

static void Main(string[] args) { 
     var strToCheck = "Address"; 
     var list = new List<Person> { new Person { Address = "Address1" }, new Person { Address = "AAAAAA" } }; 
     var adresses = FilterByAddress(list, p => p.Address.Contains(strToCheck)); 
    } 

    public static IEnumerable<Person> FilterByAddress(List<Person> list, Expression<Func<Person, bool>> predicateEx) { 
     var listParam = Expression.Parameter(typeof(IEnumerable<Person>), "list"); 
     var select = typeof(Enumerable).GetMethods().Single(m => m.Name.Equals("Where") && m.GetParameters()[1].ParameterType.GetGenericArguments().Length == 2); 
     var genericMethod = select.MakeGenericMethod(new[] { typeof(Person) }); 
     var call = Expression.Call(null, genericMethod, new Expression[] { listParam, predicateEx }); 
     var lambda = Expression.Lambda<Func<IEnumerable<Person>, IEnumerable<Person>>>(call, new[] { listParam }); 

     return lambda.Compile().Invoke(list); 
    } 

如果你有一個非常compilicated謂詞跨越到多行(一個表達式樹可以從一條線拉姆達進行評估),可以使用一個特技構建表達樹出謂詞的函數功能是這樣的:

static void Main(string[] args) { 
     var strToCheck = "Address"; 
     Func<Person, bool> predicate = p => { 
      return p.Address.Contains(strToCheck); 
     }; 

     var list = new List<Person> { new Person { Address = "Address1" }, new Person { Address = "AAAAAA" } }; 
     var adresses = FilterByAddress(list, predicate); 
    } 

    public static IEnumerable<Person> FilterByAddress(List<Person> list, Func<Person, bool> predicate) { 
     var listParam = Expression.Parameter(typeof(IEnumerable<Person>), "list"); 
     Expression<Func<Person, bool>> predicateEx = p => predicate(p); 
     var select = typeof(Enumerable).GetMethods().Single(m => m.Name.Equals("Where") && m.GetParameters()[1].ParameterType.GetGenericArguments().Length == 2); 
     var genericMethod = select.MakeGenericMethod(new[] { typeof(Person) }); 
     var call = Expression.Call(null, genericMethod, new Expression[] { listParam, predicateEx }); 
     var lambda = Expression.Lambda<Func<IEnumerable<Person>, IEnumerable<Person>>>(call, new[] { listParam }); 

     return lambda.Compile().Invoke(list); 
    } 

使用通用的方法,通過謂詞來篩選列表

static void Main(string[] args) { 
     var strToCheck = "Address"; 
     Func<Person, bool> predicate = p => { 
      return p.Address.Contains(strToCheck); 
     }; 

     var list = new List<Person> { new Person { Address = "Address1" }, new Person { Address = "AAAAAA" } }; 
     var adresses = FilterBy<Person>(list, predicate); 
    } 

    public static IEnumerable<T> FilterBy<T>(List<T> list, Func<T, bool> predicate) { 
     var listParam = Expression.Parameter(typeof(IEnumerable<T>), "list"); 
     Expression<Func<T, bool>> predicateEx = p => predicate(p); 
     var select = typeof(Enumerable).GetMethods().Single(m => m.Name.Equals("Where") && m.GetParameters()[1].ParameterType.GetGenericArguments().Length == 2); 
     var genericMethod = select.MakeGenericMethod(new[] { typeof(T) }); 
     var call = Expression.Call(null, genericMethod, new Expression[] { listParam, predicateEx }); 
     var lambda = Expression.Lambda<Func<IEnumerable<T>, IEnumerable<T>>>(call, new[] { listParam }); 

     return lambda.Compile().Invoke(list); 
    } 
} 
+2

你稱之爲**簡單**解決方案?使用的代碼的一些解釋會很好。 – Flater

+0

謝謝,我會測試它。請考慮@Flater評論 – Arian

+0

這一行:'Expression > contains = a => a.Address.Contains(strToCheck);'我想用參數實現這個代碼。我沒有訪問'Address'屬性,因爲我使用了通用的抽象類,而我的實體只是'T' – Arian