2011-02-23 6 views
28

J'essaie de joindre où prédicats et mon but est de créer la même expression que:Comment créer dynamiquement un prédicat Expression <Func <MyClass, bool >> à partir de Expression <Func <MyClass, string >>?

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

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

Cette créer la sortie suivante:

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

Cependant, ceci est défectueux puisque le paramètre pour Nom et L'espace de noms n'est pas la même chose. Si je change l'un du sélecteur d'expression:

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 avec l'utilisation de SEL1 et SEL2?

MISE À JOUR (28 feb 2011)

Je l'ai résolu en créant invoquer des expressions: Expression.Invoke si les expressions lambda SEL1 et SEL2 ne ont pas besoin nécessaire d'être 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); 
} 
+2

Avez-vous envisagé PredicateBuilder? Il est spécialement conçu pour résoudre «essayer d'ajouter des prédicats». http://www.albahari.com/nutshell/predicatebuilder.aspx –

+0

Cela semble très intéressant, je vais regarder ça. Merci Kirk! –

Répondre

53

Il est difficile de mélanger les arbres d'expression générés par le compilateur et les arbres faits à la main, précisément à cause de ce genre de chose - extraire les ParameterExpressions est délicat. 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 a certainement semble comme il devrait être un Service plutôt qu'un string.

J'ai donné qu'un essai, et il semblait travailler quand j'ai appelé lambda.Compile et exécuté sur deux échantillons Service objets ...

+0

Merci Jon! Je l'ai maintenant au travail. Serait-ce totalement faux d'obtenir le nom de membre de sel1 avec: ((MemberExpression) sel1.Body) .Member.Name; etc? –

+0

@ Torbjörn: Euh, ça dépend. Je ne peux pas vraiment dire ce que vous essayez de faire. Cela ne fonctionnerait évidemment que si le corps de 'sel1' vraiment * était * un' MemberExpression' ... et il pourrait ne pas être une propriété ... –

+0

Il semble redondant de passer 'argParam' dans' Expression.Property (argParam, " Nom ")' et 'Expression.Lambda Alisson

Questions connexes