2011-02-23 13 views
28

que tratar de anexar donde predicados y mi objetivo es crear la misma expresión:¿Cómo creo dinámicamente un predicado Expression <Func <MyClass, bool >> de Expression <Func <MyClass, string >>?

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

tengo el siguiente código:

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

Esta crear el siguiente resultado:

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

Sin embargo, esto es incorrecto ya que el parámetro para Nombre y El espacio de nombres no es lo mismo. Si cambio de una de las selector de expresión a:

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

La salida será:

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

¿Cómo puedo crear una expresión válida con el uso de sel1 y sel2?

ACTUALIZACIÓN (28 de febrero 2011)

me lo resolvió mediante la creación de invocar expresiones: Expression.Invoke por lo que la expresiones lambda sel1 y sel2 sí es necesario no es necesario ser 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); 
} 
+2

¿Usted ha considerado PredicateBuilder? Está diseñado específicamente para resolver "intentar agregar predicados". http://www.albahari.com/nutshell/predicatebuilder.aspx –

+0

Suena muy interesante, voy a ver eso. ¡Gracias, Kirk! –

Respuesta

53

¡Es Es difícil mezclar árboles de expresión generados por el compilador y hechos a mano, precisamente por este tipo de cosas: extraer los ParameterExpressions es complicado. Así que vamos a empezar desde cero:

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 aspecto importante es que he cambiado el tipo pasó a Expression.Parameter - ciertamente se ve como tiene que ser un Service en lugar de un string.

me he dado que un intento, y parece que ha funcionado cuando llamé lambda.Compile y ejecutado en un par de muestras Service objetos ...

+0

¡Gracias, Jon! Lo tengo para trabajar ahora. ¿Sería totalmente incorrecto obtener el nombre de miembro de sel1 con: ((MemberExpression) sel1.Body) .Member.Name; etc? –

+0

@ Torbjörn: Um, depende. Realmente no puedo decir lo que estás tratando de hacer. Obviamente solo funcionaría si el cuerpo de 'sel1' realmente * fuera * un' MemberExpression' ... y podría no ser una propiedad ... –

+0

Suena redundante al pasar 'argParam' dentro de' Expression.Property (argParam, " Nombre ")' y 'Expression.Lambda > (andExp, argParam);'. Creo que solo tenía que pasar a ambos 'Expression.Property()' (para las dos propiedades), pero ¿por qué deberíamos pasarlo de nuevo a 'Expression.Lambda()'? Funcionó de todos modos para mi escenario, solo tenía curiosidad;) – Alisson

Cuestiones relacionadas