Entendiendo el árbol de expresiones y la evaluación de parámetros

c# expression-trees

Pregunta

Estoy intentando modificar un árbol de expresiones que genera dinámicamente una expresión Contains que finalmente da como resultado SQL como

P IN (123, 124, 125, 200, 201)

en su lugar, verifica realizar verificaciones de rango, lo que finalmente resulta en SQL como

(P >= 123 AND P <= 125) OR (P >= 200 AND P <= 201)

Estoy basando mi solución en este post .

static public Expression<Func<TElement, bool>> 
BuildContainsExpression<TElement, TValue>(
    Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
{
    // Removed for post: Input checks and edge cases

    var equals = 
      values.Select(value => 
       (Expression)Expression.Equal(valueSelector.Body, 
                                    Expression.Constant(value, typeof(TValue))));

    var body = equals.Aggregate<Expression>((accumulate, equal) => 
                                             Expression.Or(accumulate, equal));

    return Expression.Lambda<Func<TElement, bool>>(body, p);
}

Puedo hacer que el control de rango funcione si proporciono el valor de comparación:

long testValue = 5;
List<KeyValuePair<int, int>> ranges = new List<KeyValuePair<int, int>>() 
{
    new KeyValuePair<long, long>(3, 6),
    new KeyValuePair<long, long>(10, 12),
    new KeyValuePair<long, long>(20, 20),
};

List<BinaryExpression> rangeExpressions = new List<BinaryExpression>();

foreach (var pair in ranges)
{
    var greaterThanOrEqual = 
        Expression.GreaterThanOrEqual(Expression.Constant(testValue), 
                                      Expression.Constant(pair.Key));

    var lessThanOrEqual = 
        Expression.LessThanOrEqual(Expression.Constant(testValue), 
                                   Expression.Constant(pair.Value));

    var inRange = Expression.AndAlso(greaterThanOrEqual, lessThanOrEqual);
    rangeExpressions.Add(inRange);
}

var final = 
    rangeExpressions.Aggregate<Expression>((a, b) => Expression.Or(a, b));

var result = Expression.Lambda<Func<bool>>(final).Compile()();

Sin embargo, no puedo averiguar cómo obtener el valor de comparación de la expresión pasada cuando coloco ese código en el método que se utilizará con Linq. La firma de ese método es:

Expression<Func<TElement, bool>> 
BuildRangeExpression<TElement>(
    Expression<Func<TElement, long>> valueSelector, 
    IEnumerable<long> values)

y se usa como:

Expression<MyType, bool> match = 
    BuildRangeExpression<MyType, long>(my => my.ProductCode, productCodes);

var result = db.MyTypes.Where(match);

PREGUNTA

Como puedo evaluar

Expression<Func<TElement, long>> valueSelector

de modo que pueda usar el valor pasado en BuildRangeExpression en lugar de mi valor actual codificado

long testValue = 5;

Respuesta aceptada

Creo que el código de la publicación del blog tiene exactamente lo que necesitas: todo lo que tienes que hacer es usar valueSelector.Body lugar de Expression.Constant() y también agregar el parámetro original a la expresión generada:

public static Expression<Func<TElement, bool>>
    BuildRangeExpression<TElement, TValue>(
    Expression<Func<TElement, TValue>> valueSelector,
    IEnumerable<Tuple<TValue, TValue>> values)
{
    var p = valueSelector.Parameters.Single();

    var equals = values.Select(
        tuple =>
        Expression.AndAlso(
            Expression.GreaterThanOrEqual(
                valueSelector.Body, Expression.Constant(tuple.Item1)),
            Expression.LessThanOrEqual(
                valueSelector.Body, Expression.Constant(tuple.Item2))));

    var body = equals.Aggregate(Expression.OrElse);

    return Expression.Lambda<Func<TElement, bool>>(body, p);
}

Respuesta popular

Utilice Expression.Parameter .

Crear un parámetro:

var param = Expression.Parameter(typeof(TElement), "arg")

En lugar de Expression.Constant(testvalue) , deberás poner param .

Entonces, tienes que hacer:

var result = Expression.Lambda<Func<TElement, bool>>(final, param).Compile() 


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