2017-09-24 11 views
0

Lié à: Create a Lambda Expression With 3 conditionsConvert Contient à l'expression Arbre

S'il vous plaît considérer ce code:

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

Comment puis-je convertir en arbre d'expression et d'écrire le code ci-dessus avec des expressions? Le principal problème est la conversion a.Address.Contains(strToCheck) en Expression Tree.

Edit 1) L'adresse est un champ string et strToCheck est un string

Merci

+0

Quelle est la différence entre les convertir ' .Select() 'à l'arbre d'expression et' .Contains() 'à l'arbre d'expression? Rien. Donc, si vous savez comment convertir 'Select' vous devez savoir comment convertir' Contains'. –

+0

Merci. Mais je suis nouveau pour les arbres d'Expression et il n'y a pas beaucoup et bonne ressource pour cela. Si vous connaissez la réponse s'il vous plaît aidez-moi – Arian

+0

SO contient de nombreux exemples comment écrire l'arbre d'expression qui invoque une méthode, par exemple 'Any'' Where' et beaucoup aussi. Vous pouvez vérifier quelques exemples: [first] (https://stackoverflow.com/questions/45887790/access-childrenlist-related-property-in-expression-tree) et [second] (https://stackoverflow.com/ questions/45765350/create-predicate-with-a-binaryexpression-containing-multiple-parameters). L'idée principale est que vous créez 'MethodCallExpression' à partir de' MethodInfo' correspondant. En passant, pourquoi voulez-vous convertir votre exemple en un 'ExpressionTree'? –

Répondre

2

a.Address.Contains(strToCheck) représente un appel-string.Containsméthode d'instance sur a.Addressexemple avec strToCheckarguments.

La façon la plus simple de construire l'expression correspondante est d'utiliser les éléments suivants Expression.Call overload:

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

comme celui-ci (en utilisant les termes de la question liée):

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

c'est EXCELLENT. Merci – Arian

0

Vous ne précisaient pas le type de MyTbl,
J'ai donc créé une solution simple en utilisant seulement une liste d'objets.

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; } 
    } 
} 

Si vous souhaitez utiliser un filtre par prédicats vous pouvez passer un Expresssion<Func<Person, bool>> en tant que paramètre (une ligne)

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); 
    } 

Si vous avez un prédicat très compilicated qui couvre plusieurs lignes (un arbre d'expression peut être évaluée à partir d'une seule ligne lambda), vous pouvez utiliser une astuce pour construire un arbre d'expression sur le prédicat Func comme ceci:

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); 
    } 

En utilisant l'approche générique pour filtrer une liste par prédicats

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

Vous appelez cela une solution ** simple **? Une explication du code utilisé serait bien. – Flater

+0

Merci, je vais le tester. S'il vous plaît envisager @Flater commenter – Arian

+0

Cette ligne: 'Expression > contient = a => a.Address.Contains (strToCheck)' Je veux mettre en œuvre ce code avec le paramètre. J'ai pas accès à la propriété 'parce que je 'Adresse classe abstraite générique et mon entité est juste' T' – Arian