2011-03-19 5 views
5

Quiero combinar dos LambdaExpressions sin compilarlos.Cómo construir una LambdaExpression a partir de una LambdaExpression existente sin compilar

Esto es lo que parece si hago compilarlas:

public Expression<Func<TContainer,bool>> CreatePredicate<TContainer,TMember>(
     Expression<Func<TContainer,TMember>> getMemberExpression, 
     Expression<Func<TMember,bool>> memberPredicateExpression) 
    { 
     return x => memberPredicateExpression.Compile()(getMemberExpression.Compile()(x)); 
    } 

Eso no es obviamente la manera más rápida para obtener la expresión de destino desde los argumentos proporcionados. Además, lo hace incompatible con proveedores de consultas como LINQ to SQL que no son compatibles con las llamadas al método C#.

Según lo que he leído, parece que el mejor enfoque es construir una clase ExpressionVisitor. Sin embargo, parece que podría ser una tarea bastante común. ¿Alguien sabe de una base de código fuente abierta existente que proporciona este tipo de funcionalidad? Si no, ¿cuál es la mejor manera de acercarse al ExpressionVisitor para que sea lo más genérico posible?

Respuesta

4

No sé si es la mejor manera, pero se puede hacer algo así:

public Expression<Func<TContainer,bool>> CreatePredicate<TContainer,TMember>(
    Expression<Func<TContainer,TMember>> getMemberExpression, 
    Expression<Func<TMember,bool>> memberPredicateExpression) 
{ 
    ParameterExpression x = Expression.Parameter(typeof(TContainer), "x"); 
    return Expression.Lambda<Func<TContainer, bool>>(
     Expression.Invoke(
      memberPredicateExpression, 
      Expression.Invoke(
       getMemberExpression, 
       x)), 
     x); 
} 

Uso:

var expr = CreatePredicate(
    (Foo f) => f.Bar, 
    bar => bar % 2 == 0); 

Resultado:

x => Invoke(bar => ((bar % 2) == 0), Invoke(f => f.Bar, x)) 

supongo sería mejor obtener algo como x => x.Bar % 2 == 0, pero probablemente sería significativamente más difícil ...


EDIT: en realidad no era tan duro con un visitante de la expresión:

public Expression<Func<TContainer,bool>> CreatePredicate<TContainer,TMember>(
    Expression<Func<TContainer,TMember>> getMemberExpression, 
    Expression<Func<TMember,bool>> memberPredicateExpression) 
{ 
    return CombineExpressionVisitor.Combine(
     getMemberExpression, 
     memberPredicateExpression); 
} 

class CombineExpressionVisitor : ExpressionVisitor 
{ 
    private readonly ParameterExpression _parameterToReplace; 
    private readonly Expression _replacementExpression; 
    private CombineExpressionVisitor(ParameterExpression parameterToReplace, Expression replacementExpression) 
    { 
     _parameterToReplace = parameterToReplace; 
     _replacementExpression = replacementExpression; 
    } 

    public static Expression<Func<TSource, TResult>> Combine<TSource, TMember, TResult>(
     Expression<Func<TSource, TMember>> memberSelector, 
     Expression<Func<TMember, TResult>> resultSelector) 
    { 
     var visitor = new CombineExpressionVisitor(
      resultSelector.Parameters[0], 
      memberSelector.Body); 
     return Expression.Lambda<Func<TSource, TResult>>(
      visitor.Visit(resultSelector.Body), 
      memberSelector.Parameters); 
    } 

    protected override Expression VisitParameter(ParameterExpression parameter) 
    { 
     if (parameter == _parameterToReplace) 
      return _replacementExpression; 
     return base.VisitParameter(parameter); 
    } 
} 

Se da la siguiente expresión:

f => ((f.Bar % 2) == 0) 
+0

Gracias - de hecho voy por lo que se dijo que era "significativamente más difícil". Sin embargo, esto podría terminar siendo un buen punto de partida. – smartcaveman

+0

@Thomas, bien, esto es lo que estoy pensando: haga lo que sugirió, pero primero compare los ParameterExpressions de LambdaExpression para asegurarse de que no haya nombres de parámetros duplicados. Luego, haga una visita a 'ExpressionVisitor'' ExpressionType.Invoke', seleccione los argumentos y cree un nuevo 'getMemberExpression.Body', con los valores de argumento reemplazando cada ocurrencia de parámetro. Luego devuelve el cuerpo de LambdaExpression. ¿Cómo suena ésto para ti? – smartcaveman

+0

@smartcaveman, finalmente escribí una solución ExpressionVisitor, que no necesita Invoke en absoluto. Ver mi respuesta actualizada –

Cuestiones relacionadas