Convert Contains To Tree Expression

c# c#-6.0 expression-trees lambda linq

Question

En relation Avec : Créer une expression Lambda avec 3 conditions

Veuillez considérer ce code:

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

Comment puis-je convertir ceci en Expression Tree et écrire le code ci-dessus avec Expressions? Le problème principal est la conversion de a.Address.Contains(strToCheck) en Expression Tree .

Éditer 1) Adresse est un champ de string et strToCheck est une string

Merci

Réponse acceptée

a.Address.Contains(strToCheck) représente un appel à string.Contains méthode d'instance sur a.Address exemple avec strToCheck argument.

Le moyen le plus simple de générer l'expression correspondante consiste à utiliser la surcharge Expression.Call suivante:

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

comme ceci (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
);

Réponse populaire

Vous n'avez pas spécifié le type de myTbl,
J'ai donc créé une solution simple en utilisant simplement 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édicat, vous pouvez passer un Expresssion<Func<Person, bool>> 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 compilé qui s'étend sur plusieurs lignes (un arbre d'expression peut être évalué à partir d'un lambda à une ligne), vous pouvez utiliser une astuce pour construire un arbre d'expression à partir du 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);
    }

Utilisation de l'approche générique pour filtrer une liste par prédicat

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



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi