Comment créer dynamiquement une expression > prédicat de Expression >?

c# expression-trees linq linq-expressions

Question

J'essaie d'annexer 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, ceci 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 créer une expression valide avec l'utilisation de sel1 et sel2 ?

MISE À JOUR (28 février 2011)

Je l'ai résolu en créant des expressions d'invocation: Expression.Invoke afin que les expressions lambda sel1 et sel2 n'aient pas besoin d'être un 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);
}

Réponse acceptée

Il est difficile de mélanger des arbres d'expression générés par le compilateur et ceux fabriqués à la main, précisément à cause de ce genre de chose - l'extraction de ParameterExpressions est délicate. Alors commençons à partir de 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 semble bien que cela devrait être un Service plutôt qu'une string .

J'ai essayé, et cela a semblé fonctionner lorsque j'ai appelé lambda.Compile et que je l' lambda.Compile exécuté sur quelques exemples d'objets Service .


Réponse populaire

Vous pouvez créer un arbre d’expression pour les types nullables, supposons que vous ayez un champ nullable BoardId, vous pouvez créer un arbre d’expression dynamiquement comme ceci

var nameValue = "BoardId = 111";

vous devez déterminer le premier type de propriété, Nullable 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 à cette fin

  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 si sa chaîne est créée puis à créer une expression pour une chaîne.

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



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow