web-dev-qa-db-fra.com

Comment créer dynamiquement un prédicat Expression <Func <MyClass, bool >> à partir d'Expression <Func <MyClass, string >>?

J'essaye d'ajouter où les prédicats et mon objectif est de créer la même expression que:

Services.Where(s => s.Name == "Modules" && s.Namespace == "Namespace");

J'ai le code suivant:

Expression<Func<Service,string>> sel1 = s => s.Name;
Expression<Func<Service,string>> sel2 = s => s.Namespace;

var val1 = Expression.Constant("Modules");
var val2 = Expression.Constant("Namespace");

Expression e1 = Expression.Equal(sel1.Body, val1);
Expression e2 = Expression.Equal(sel2.Body, val2);
var andExp = Expression.AndAlso(e1, e2);

ParameterExpression argParam = Expression.Parameter(typeof(string), "s");
var lambda = Expression.Lambda<Func<string, bool>>(andExp, argParam);

Cela crée la sortie suivante:

s => ((s.Name == "Modules") AndAlso (s.Namespace == "Namespace"))

Cependant, cela est défectueux car le paramètre pour Name et Namespace n'est pas le même. Si je change l'un des sélecteurs d'expression en:

Expression<Func<Service,string>> sel2 = srv => srv.Namespace;

La sortie sera:

s => ((s.Name == "Modules") AndAlso (srv.Namespace == "Namespace"))

Comment puis-je créer une expression valide en utilisant sel1 et sel2?

MISE À JOUR (28 février 2011)

Je l'ai résolu en créant des expressions d'invocation: Expression.Invoke donc les expressions lambda sel1 et sel2 n'ont pas nécessairement besoin d'être une MemberExpression:

Expression<Func<Service,string>> sel1 = s => s.Name;
Expression<Func<Service,string>> sel2 = srv => srv.Namespace;

var val1 = Expression.Constant("Modules");
var val2 = Expression.Constant("Namespace");

Expression<Func<Service, bool>> lambda = m => true;
var modelParameter = lambda.Parameters.First();

// sel1 predicate
{
    var invokedExpr = Expression.Invoke(sel1, modelParameter);
    var binaryExpression = Expression.Equal(invokedExpr, val1);
    lambda = Expression.Lambda<Func<Service, bool>>(Expression.AndAlso(binaryExpression, lambda.Body), lambda.Parameters);
}
// sel2 predicate
{
    var invokedExpr = Expression.Invoke(sel2, modelParameter);
    var binaryExpression = Expression.Equal(invokedExpr, val2);
    lambda = Expression.Lambda<Func<Service, bool>>(Expression.AndAlso(binaryExpression, lambda.Body), lambda.Parameters);
}
39
Torbjörn Hansson

Il est difficile de mélanger les arbres d'expressions générés par le compilateur et ceux créés à la main, précisément à cause de ce genre de chose - extraire les ParameterExpressions est délicat. Commençons donc à zéro:

ParameterExpression argParam = Expression.Parameter(typeof(Service), "s");
Expression nameProperty = Expression.Property(argParam, "Name");
Expression namespaceProperty = Expression.Property(argParam, "Namespace");

var val1 = Expression.Constant("Modules");
var val2 = Expression.Constant("Namespace");

Expression e1 = Expression.Equal(nameProperty, val1);
Expression e2 = Expression.Equal(namespaceProperty, val2);
var andExp = Expression.AndAlso(e1, e2);

var lambda = Expression.Lambda<Func<Service, bool>>(andExp, argParam);

Un aspect important que j'ai changé est le type passé à Expression.Parameter - il est certainement ressemble comme si cela devait être un Service plutôt qu'un string.

J'ai essayé, et cela a semblé fonctionner quand j'ai appelé lambda.Compile et exécuté sur quelques exemples Service objets ...

66
Jon Skeet

Vous pouvez créer un arbre d'expression pour les types nullables, supposons que vous ayez un champ nullable BoardId, vous pouvez créer dynamiquement un arbre d'expression comme celui-ci

var nameValue = "BoardId = 111";

vous devez déterminer le premier type de propriété, qu'il soit nul ou non

Sous le code, créez une expression d'arborescence dynamique pour les types nullable et non nullable

 public static Expression<Func<T, bool>> BuildWhereExpression<T>(string nameValueQuery ) where  T : class 
        {
            Expression<Func<T, bool>> predicate = null;
            PropertyInfo prop = null;
            var fieldName = nameValueQuery.Split("=")[0];
            var fieldValue = nameValueQuery.Split("=")[1];
            var properties = typeof(T).GetProperties();
            foreach (var property in properties)
            {
                if (property.Name.ToLower() == fieldName.ToLower())
                {
                    prop = property;
                }
            } 
            if (prop != null)
            {
                var isNullable = prop.PropertyType.IsNullableType();
                var parameter = Expression.Parameter(typeof(T), "x");
                var member = Expression.Property(parameter, fieldName); 

                if (isNullable)
                {
                    var filter1 =
                        Expression.Constant(
                            Convert.ChangeType(fieldValue, member.Type.GetGenericArguments()[0]));
                    Expression typeFilter = Expression.Convert(filter1, member.Type);
                    var body = Expression.Equal(member, typeFilter);  
                    predicate = Expression.Lambda<Func<T, bool>>(body, parameter);  
                }
                else
                {
                    if (prop.PropertyType == typeof(string) && likeOerator.ToLower() == "like")
                    {
                        var parameterExp = Expression.Parameter(typeof(T), "type");
                        var propertyExp = Expression.Property(parameterExp, prop);
                        MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
                        var someValue = Expression.Constant(fieldValue, typeof(string));
                        var containsMethodExp = Expression.Call(propertyExp, method, someValue);
                        predicate = Expression.Lambda<Func<T, bool>>(containsMethodExp, parameterExp);
                    }
                    else
                    {
                        var constant = Expression.Constant(Convert.ChangeType(fieldValue, prop.PropertyType));
                        var body = Expression.Equal(member, constant);  
                        predicate = Expression.Lambda<Func<T, bool>>(body, parameter); `enter code here`
                    }
                }
            }
            return predicate;
        }

1- Cette solution vérifie d'abord la valeur Nullable et génère l'expression. Voici comment vous pouvez déterminer si le type est Nullable. J'ai créé une méthode d'extension à cet effet

  public static bool IsNullableType(this Type type) {  return
    type.IsGenericType &&
    (type.GetGenericTypeDefinition().Equals(typeof(Nullable<>))); }

2- la deuxième étape consiste à vérifier le type de sa chaîne puis à créer une expression pour une chaîne.

3- La troisième étape consiste à vérifier si la valeur n'est pas annulable ni la chaîne, puis créer une expression en utilisant égal

3
Aamir