2011-03-25 5 views
11

Estoy intentando construir dinámicamente expresiones basadas en un objeto de especificación.Reemplazar el nombre del parámetro en el cuerpo de una expresión

He creado una clase ExpressionHelper que tiene una expresión privada de este modo:

private Expression<Func<T, bool>> expression; 

public ExpressionHelper() 
{ 
    expression = (Expression<Func<T, bool>>)(a => true); 
} 

Y entonces algunos métodos sencillos de la siguiente manera:

public void And(Expression<Func<T,bool>> exp); 

Estoy luchando con el cuerpo de la Y el método. Básicamente, quiero extraer el cuerpo de exp, reemplazar todos los parámetros con los de expression y luego anexarlo al final del cuerpo expression como AndAlso.

he hecho esto:

var newBody = Expression.And(expression.Body,exp.Body); 

expression = expression.Update(newBody, expression.Parameters); 

Pero que termina con mi expresión con este aspecto:

{ a => e.IsActive && e.IsManaged } 

¿Existe una manera más sencilla de hacer esto? ¿O cómo puedo arrancar esas e's y reemplazarlas con a's?

Respuesta

20

El enfoque más simple es aquí Expression.Invoke, por ejemplo:

public static Expression<Func<T, bool>> AndAlso<T>(
    Expression<Func<T, bool>> x, Expression<Func<T, bool>> y) 
{ 
    return Expression.Lambda<Func<T, bool>>(
     Expression.AndAlso(x.Body, Expression.Invoke(y, x.Parameters)), 
     x.Parameters); 
} 

Esto funciona muy bien para LINQ a Objetos y LINQ to SQL, pero no es compatible con EF. Para EF necesitarás usar un visitante para reescribir el árbol, por desgracia.

Utilizando el código de: Combining two lambda expressions in c#

public static Expression<Func<T, bool>> AndAlso<T>(
    Expression<Func<T, bool>> x, Expression<Func<T, bool>> y) 
{ 
    var newY = new ExpressionRewriter().Subst(y.Parameters[0], x.Parameters[0]).Inline().Apply(y.Body); 

    return Expression.Lambda<Func<T, bool>>(
     Expression.AndAlso(x.Body, newY), 
     x.Parameters); 
} 

O en .NET 4.0, utilizando ExpressionVisitor:

class ParameterVisitor : ExpressionVisitor 
{ 
    private readonly ReadOnlyCollection<ParameterExpression> from, to; 
    public ParameterVisitor(
     ReadOnlyCollection<ParameterExpression> from, 
     ReadOnlyCollection<ParameterExpression> to) 
    { 
     if(from == null) throw new ArgumentNullException("from"); 
     if(to == null) throw new ArgumentNullException("to"); 
     if(from.Count != to.Count) throw new InvalidOperationException(
      "Parameter lengths must match"); 
     this.from = from; 
     this.to = to; 
    } 
    protected override Expression VisitParameter(ParameterExpression node) 
    { 
     for (int i = 0; i < from.Count; i++) 
     { 
      if (node == from[i]) return to[i]; 
     } 
     return node; 
    } 
} 
public static Expression<Func<T, bool>> AndAlso<T>(
     Expression<Func<T, bool>> x, Expression<Func<T, bool>> y) 
{ 
    var newY = new ParameterVisitor(y.Parameters, x.Parameters) 
       .VisitAndConvert(y.Body, "AndAlso"); 
    return Expression.Lambda<Func<T, bool>>(
     Expression.AndAlso(x.Body, newY), 
     x.Parameters); 
} 
+0

Maldición, acabo de invocación de trabajo en la memoria y se prueba en EF cuando esto sucedió. Gracias. –

+0

@robert IIRC Invoke no funciona en EF, por lo que deberá utilizar la segunda versión –

+0

Su enfoque funciona, pero ahora tengo su ReWriter asombrosamente complejo y no probado en mi código base. :(Viendo lo inteligente que se ve, lo voy a guardar hasta que lo entiendo! –

Cuestiones relacionadas