Cómo crear una cláusula de expresión AND a partir de dos expresiones

c# expression-trees

Pregunta

Estoy tratando de crear una cláusula where para mi vista usando LINQ.

Pude crear una sola columna donde la cláusula y ahora me gustaría crear varias columnas donde las cláusulas ...

He visto código para implementar en .Net 4 y superior, pero como tengo que usar .Net 3.5, necesito una solución rápida para esto. así que lo que estoy tratando de hacer es ...

 Expression leftexp = {tag=>((tag.id=2)||(tag.id=3))}
 Expression rightexp = {tag=>((tag.uid="MU")||(tag.uid="ST"))}

De estas dos expresiones me gustaría crear.

 BinaryExpression be = {tag=>((tag.id=2)||(tag.id=3))} && 
                       {tag=>((tag.uid="MU")||(tag.uid="ST"))} 

algo como esto que podría pasar a mi cláusula where en LINQ.

Intenté usar Expression.And (leftexp, rightexp)

pero consiguió el error ..

El operador binario Y no está definido para los tipos.
'System.Func 2[WebApplication1.View_MyView,System.Boolean]' and 'System.Func 2 [WebApplication1.View_MyView, System.Boolean]'.

La expresión es nueva para mí y podría haber visto demasiado código, por lo que estoy un poco confundido sobre cómo hacer esto ... realmente agradecería que me orientaras en la dirección correcta.

Respuesta aceptada

No puede hacer eso sin volver a escribir los dos árboles de expresiones completos en uno nuevo completo.

Motivo: los objetos de expresión de parámetros deben ser los mismos para todo el árbol de expresiones. Si combina los dos, tiene dos objetos de expresión de parámetro para el mismo parámetro, que no funcionará.

Se muestra con el siguiente código:

Expression<Func<Tab, bool>> leftexp = tag => ((tag.id == 2) || (tag.id == 3));
Expression<Func<Tab, bool>> rightexp = tag => ((tag.uid == "MU") || (tag.uid == "ST"));

Expression binaryexp = Expression.AndAlso(leftexp.Body, rightexp.Body);
ParameterExpression[] parameters = new ParameterExpression[1] {
    Expression.Parameter(typeof(Tab), leftexp.Parameters.First().Name)
};
Expression<Func<Tab, bool>> lambdaExp = Expression.Lambda<Func<Tab, bool>>(binaryexp, parameters);

var lambda = lambdaExp.Compile();

Esto falla en la llamada lambdaExp.Compile (), que da la siguiente excepción:

Lambda Parameter not in scope

Esto se debe al hecho de que básicamente estoy reutilizando las expresiones leftexp y rightexp, pero tienen diferentes expresiones de parámetros, las cuales no son dadas por mí a Expression.Lambda<Func<Tab>>(...) llamar. En el fondo de leftexp y rightexp hay objetos de expresión de parámetros que deben coincidir con el dado a la llamada Expression.Lambda<Func<Tab>>(...) .

Para resolver esto, ha recreado la expresión completa utilizando una nueva expresión-parámetro (única) para la etiqueta de parámetro.

Consulte aquí para obtener más información sobre el problema.


Respuesta popular

La reescritura de expresiones se ha simplificado con la adición de ExpressionVisitor a BCL. Con algunos ayudantes la tarea se vuelve casi trivial.

Aquí hay una clase de visitante que uso para aplicar un delegado a los nodos del árbol:

internal sealed class ExpressionDelegateVisitor : ExpressionVisitor {

    private readonly Func<Expression , Expression> m_Visitor;
    private readonly bool m_Recursive;

    public static Expression Visit ( Expression exp , Func<Expression , Expression> visitor , bool recursive ) {
        return new ExpressionDelegateVisitor ( visitor , recursive ).Visit ( exp );
    }

    private ExpressionDelegateVisitor ( Func<Expression , Expression> visitor , bool recursive ) {
        if ( visitor == null ) throw new ArgumentNullException ( nameof(visitor) );
        m_Visitor = visitor;
        m_Recursive = recursive;
    }

    public override Expression Visit ( Expression node ) {
        if ( m_Recursive ) {
            return base.Visit ( m_Visitor ( node ) );
        }
        else {
            var visited = m_Visitor ( node );
            if ( visited == node ) return base.Visit ( visited );
            return visited;
        }
    }

}

Y aquí están los métodos de ayuda para simplificar la reescritura:

public static class SystemLinqExpressionsExpressionExtensions {

    public static Expression Visit ( this Expression self , Func<Expression , Expression> visitor , bool recursive = false ) {
        return ExpressionDelegateVisitor.Visit ( self , visitor , recursive );
    }

    public static Expression Replace ( this Expression self , Expression source , Expression target ) {
        return self.Visit ( x => x == source ? target : x );
    }

    public static Expression<Func<T , bool>> CombineAnd<T> ( this Expression<Func<T , bool>> self , Expression<Func<T , bool>> other ) {
        var parameter = Expression.Parameter ( typeof ( T ) , "a" );
        return Expression.Lambda<Func<T , bool>> (
            Expression.AndAlso (
                self.Body.Replace ( self.Parameters[0] , parameter ) ,
                other.Body.Replace ( other.Parameters[0] , parameter )
            ) ,
            parameter
        );
    }

}

Lo que permite combinar expresiones como esta:

static void Main () {
    Expression<Func<int , bool>> leftExp = a => a > 3;
    Expression<Func<int , bool>> rightExp = a => a < 7;
    var andExp = leftExp.CombineAnd ( rightExp );
}

ACTUALIZAR:

En el caso de que ExpressionVisitor no esté disponible, su fuente se publicó hace un tiempo aquí . Nuestra biblioteca usó esa implementación hasta que hayamos migrado a .NET 4.



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow