Выражение Ошибка метода GreaterThanOrEqual - PullRequest
0 голосов
/ 17 января 2019

Я создал статический класс и попытался реализовать динамический запрос с общими параметрами. Ниже мое определение интерфейса и определение класса:

public interface IRangeValue<T> where T : struct
{
    Nullable<T> High{get;set;}
    Nullable<T> Low { get; set; }
}

public class DataRangeT<T> : IRangeValue<T>
    where T : struct
{
    Nullable<T> _high;
    Nullable<T> _low;
    public Nullable<T> High { get { return _high; } set { _high = value; } }
    public Nullable<T> Low { get { return _low; } set { _low = value; } }

}

Затем я создал статический метод для реализации динамического запроса:

public static class ExpressionHelperT<K>
    where K : struct
{
    public static Expression<Func<T, bool>> RangeCompare<T>(Expression<Func<T, K>> selector, IRangeValue<K> patten)
    {
        Expression<Func<T, bool>> predicate = PredicateBuilder.True<T>();
        if (patten.High.HasValue)
        {
            predicate = predicate.And<T>(Expression.Lambda<Func<T, bool>>(Expression.LessThanOrEqual(selector, Expression.Constant(patten.High.Value, typeof(K)))));
        }
        if (patten.Low.HasValue)
        {
            predicate = predicate.And<T>(Expression.Lambda<Func<T, bool>>(Expression.GreaterThanOrEqual(selector, Expression.Constant(patten.Low.Value, typeof(K)))));
        }
        return predicate;
    }

}

метод А определяется следующим образом:

public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1,Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
    }

Теперь я пытаюсь проверить мой RangeCompare<T> метод, я вызываю метод, как показано ниже:

class Program
{
    static void Main(string[] args)
    {
        DataRangeT<DateTime> dataRange = new DataRangeT<DateTime>();

        dataRange.High = null;
        dataRange.Low = DateTime.Today;

        List<DateTime> dates = new List<DateTime>();
        dates.Add(new DateTime(2018, 1, 1));
        dates.Add(new DateTime(2018, 2, 1));
        dates.Add(new DateTime(2018, 3, 1));
        dates.Add(new DateTime(2018, 4, 1));
        dates.Add(new DateTime(2018, 5, 1));
        dates.Add(new DateTime(2018, 6, 1));
        dates.Add(new DateTime(2018, 7, 1));
        dates.Add(new DateTime(2018, 8, 1));
        dates.Add(new DateTime(2018, 9, 1));

        List<DateTime> results = dates.Where<DateTime>(ExpressionHelperT<DateTime>.RangeCompare<DateTime>(c => c.Date, dataRange)).ToList();

        foreach (DateTime dt in results)
        {
            Console.WriteLine(dt.ToShortDateString());
        }
        Console.ReadLine();
    }
}

Но compliter всегда сообщал мне об ошибке GreaterThanOrEqual, такого определения двоичного оператора между System.Func`2 [System.DateTime, System.DateTime] »и« System.DateTime »нет. Эта ошибка отображается ниже:

predicate = predicate.And<T>(Expression.Lambda<Func<T, bool>>(Expression.GreaterThanOrEqual(selector, Expression.Constant(patten.Low.Value, typeof(K)))));

Почему?

1 Ответ

0 голосов
/ 17 января 2019

Поскольку компилятор на самом деле корректен: вы пытаетесь выполнить >= между лямбда-числом и числом:

Expression.GreaterThanOrEqual(selector /* this is a lambda*/, Expression.Constant(...))

Я действительно думаю, что вы должны стараться избегать вложенных лямбд как можно больше. Лучший способ добиться этого (imho) - заменить выражения параметров во второй лямбде соответствующими параметрами первой лямбды, а затем объединить тела двух лямбда-выражений следующим образом:

    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        var replacer = new ParameterReplaceVisitor(expr2.Parameters[0], expr1.Parameters[0]);
        return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, replacer.Visit(expr2.Body)),
            expr1.Parameters[0]);
    }

    private class ParameterReplaceVisitor : ExpressionVisitor
    {
        private ParameterExpression _oldParameter;
        private ParameterExpression _newParameter;

        public ParameterReplaceVisitor(ParameterExpression oldParameter, ParameterExpression newParameter)
        {
            _oldParameter = oldParameter;
            _newParameter = newParameter;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (node == _oldParameter)
            {
                return _newParameter;
            }
            else
            {
                return node;
            }
        }
    }

Редактировать: Преимущество этого решения, которое позволяет избежать лямбда-выражений, состоит в том, что реализация Linq, скорее всего, поймет, что вы хотели сделать, и переведет это, например. в SQL. Я не уверен, что, например, EF попытается перевести вызовы лямбда-выражений в SQL, поскольку маловероятно, что это действительно работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...