2009-05-10 7 views
26

¿Cómo hago para usar un árbol de expresión para crear dinámicamente un predicado que se ve algo como ...¿Cómo creo dinámicamente un predicado Expression <Func <MyClass, bool >>?

(p.Length== 5) && (p.SomeOtherProperty == "hello") 

De modo que pueda pegarse el predicado en una expresión lambda como tal ...

q.Where(myDynamicExpression)... 

Solo necesito apuntar en la dirección correcta.

Actualización: Lo siento amigos, dejé fuera el hecho de que quiero que el predicado tenga varias condiciones como las anteriores. Perdón por la confusion.

Respuesta

45

original

así:

var param = Expression.Parameter(typeof(string), "p"); 
    var len = Expression.PropertyOrField(param, "Length"); 
    var body = Expression.Equal(
     len, Expression.Constant(5)); 

    var lambda = Expression.Lambda<Func<string, bool>>(
     body, param); 

Actualizado

re (p.Length== 5) && (p.SomeOtherProperty == "hello"):

var param = Expression.Parameter(typeof(SomeType), "p"); 
var body = Expression.AndAlso(
     Expression.Equal(
      Expression.PropertyOrField(param, "Length"), 
      Expression.Constant(5) 
     ), 
     Expression.Equal(
      Expression.PropertyOrField(param, "SomeOtherProperty"), 
      Expression.Constant("hello") 
     )); 
var lambda = Expression.Lambda<Func<SomeType, bool>>(body, param); 
+0

Gracias, pero estúpidamente me olvidó mencionar que me gustaría leer el predicado como ... (p.Length == 5) && (p.SomeOtherProperty == "hola"). En otras palabras, ¿cómo encadenar las condiciones? Lo siento por no haber sido claro – Senkwe

+0

Muchas gracias por la actualización. Parece ser lo que estaba buscando. Gracias – Senkwe

+0

@Mark Gravell: si no tuviéramos 'SomeType' cómo podemos crear lambda. por ejemplo: tenemos simplemente Type 'TyepOfEntity = Assembly.GetType (string.Format (" Smartiz.Data.{0} ", EntityName));' –

0

Puede crear una instancia de la expresión y verla con un visualizador Expression Tree. Hay uno en los ejemplos de Visual Studio: puedes compilarlo y luego ponerlo en una carpeta específica.

Eso le dará un pequeño arbolito que le muestra cómo se compone una expresión. Entonces podría construir tal expresión con los métodos estáticos del objeto Expression.

9

Para combinar varios predicados con el operador &&, los une de dos en dos.

Así que si usted tiene una lista de objetos de expresión llamados predicates, hacer esto:

Expression combined = predicates.Aggregate((l, r) => Expression.AndAlso(l, r)); 
1

Para asociar la expresión Lambda entre sí: Otra forma es utilizar el siguiente código. Es más flexible que la respuesta de Schotime en mi consejo y funciona perfectamente. Sin Pepitas externos necesarios

Marco 4,0

// Usage first.Compose(second, Expression.And) 
    public static Expression<T> Compose<T>(this Expression<T> First, Expression<T> Second, Func<Expression, Expression, Expression> Merge) 
    { 
     // build parameter map (from parameters of second to parameters of first) 
     Dictionary<ParameterExpression,ParameterExpression> map = First.Parameters.Select((f, i) => new { f, s = Second.Parameters[i] }).ToDictionary(p => p.s, p => p.f); 

     // replace parameters in the second lambda expression with parameters from the first 
     Expression secondBody = ParameterRebinder.ReplaceParameters(map, Second.Body); 

     // apply composition of lambda expression bodies to parameters from the first expression 
     return Expression.Lambda<T>(Merge(First.Body, secondBody), First.Parameters); 
    } 

    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> First, Expression<Func<T, bool>> Second) 
    { 
     return First.Compose(Second, Expression.And); 
    } 

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> First, Expression<Func<T, bool>> second) 
    { 
     return First.Compose(second, Expression.Or); 
    } 


public class ParameterRebinder : ExpressionVisitor 
{ 
    private readonly Dictionary<ParameterExpression, ParameterExpression> map; 

    public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map) 
    { 
     this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>(); 
    } 

    public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp) 
    { 
     return new ParameterRebinder(map).Visit(exp); 
    } 

    protected override Expression VisitParameter(ParameterExpression p) 
    { 
     ParameterExpression replacement; 
     if (map.TryGetValue(p, out replacement)) 
     { 
      p = replacement; 
     } 
     return base.VisitParameter(p); 
    } 
} 
Cuestiones relacionadas