Подтвердить что ты не робот

Expression.GreaterThan терпит неудачу, если один операнд имеет тип NULL, другой не имеет значения NULL

Я создаю динамический linq, и у меня возникают проблемы со следующим исключением:

Бинарный оператор GreaterThanOrEqual не определен для типов 'System.Nullable`1 [System.DateTime] и 'System.DateTime'

Я понимаю, почему, потому что мой тип поля является нулевым, а Im - в DateTime.Now по существу.

Поэтому, пытаясь решить эту проблему, я пробовал

System.Nullable<DateTime> now;
now = DateTime.Now;

Но результирующий тип является объектом, не подлежащим обнулению, и, следовательно, все еще дает мне указанное выше исключение.

Любые предложения?!

Обновление. Для получения более подробных сведений теперь переменная now становится недействительной, когда она установлена, а не остается как NULL DateTime, поэтому match генерирует исключение

Обновление: фактический код можно увидеть в проекте CodePlex:

http://webquarters.codeplex.com/SourceControl/changeset/view/36529#574700

Оскорбительная строка ~ 145

fExp = Expression.GreaterThanOrEqual(fExpLeft, fExpRight);
4b9b3361

Ответ 1

Проблема заключается в том, что библиотека выражений бросает исключение, если заданы два аргумента несоответствующей нулевой вероятности. Здесь прост:

Expression<Func<DateTime?>> ex1 = ()=>DateTime.Now;
Expression<Func<DateTime>> ex2 = ()=>DateTime.Now;
var ex3 = Expression.GreaterThan(ex1.Body, ex2.Body);

Мне непонятно, является ли это ошибкой или нет; правила из С# требуют, чтобы в этом случае операнд, не содержащий NULL, был преобразован в значение NULL, и использовалась форма сравнения с отменой до нуля. Однако библиотека дерева выражений не обязана следовать правилам С#, потому что, конечно, библиотека дерева выражений может использоваться для представления выражений С#, выражений Python, выражений JScript, выражений VB и т.д.; он не может следовать всем правилам всех возможных языков.

Но, несмотря на это, похоже, что это может быть ошибка, поэтому я отправлю ее команде дерева выражений и посмотрю, что они говорят. В то же время вы можете легко обойти его, указав свой собственный вспомогательный метод, который исправляет операнды. Быстрый эскиз:

    static Expression MyGreaterThan(Expression e1, Expression e2)
    {
        if (IsNullableType(e1.Type) && !IsNullableType(e2.Type))
            e2 = Expression.Convert(e2, e1.Type);
        else if (!IsNullableType(e1.Type) && IsNullableType(e2.Type))
            e1 = Expression.Convert(e1, e2.Type);
        return Expression.GreaterThan(e1, e2);
    }
    static bool IsNullableType(Type t)
    {
        return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>);
    }

Однако обратите внимание, что это не проверяет, что типы e1 и e2 отличаются только обнуляемостью; если вы передадите значение с нулевым int и двойным выражением, не имеющим значения NULL, возникают плохие вещи. Я оставляю это как упражнение для реализации лучшей логики, которая проверяет, имеют ли два выражения тип, который отличается только значением nullability.

Ответ 2

где бы вы ни сравнивали, измените сравнение следующим образом:

(nullableDT >= DT)

Для

(nullableDT != null && nullableDT.Value >= DT)

Edit:

В соответствии с вашим комментарием, Напишите функцию, которая принимает 2 объекта, внутри функции проверяет, являются ли они типами NULL, и проверяет значение null, а затем сравнивает значения. Эта функция, вероятно, будет использовать код, подобный ^.

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

Думаю, вам может понадобиться сделать шаг назад на этом на минуту. Если вы разместите больше кода, мы можем помочь вам еще.

Ответ 3

Я нашел решение, которое работает в рамках .Net. Здесь метод, который все еще выполняется, но он работает, и работает для Linq для Entities:

public PagedViewModel<T> Filter<TValue>(Expression<Func<T, TValue>> predicate, FilterType filterType = FilterType.Equals) {
        var name = (predicate.Body as MemberExpression ?? ((UnaryExpression)predicate.Body).Operand as MemberExpression).Member.Name;            
        var value = Expression.Constant(ParamsData[name].To<TValue>(), typeof (T).GetProperty(name).PropertyType);                        

        // If nothing has been set for filter, skip and don't filter data.
        ViewData[name] = m_QueryInternal.Distinct(predicate.Compile()).ToSelectList(name, name, ParamsData[name]);
        if (string.IsNullOrWhiteSpace(ParamsData[name]))
            return this;

        var nameExpression = Expression.Parameter(typeof(T), name);
        var propertyExpression = Expression.Property(nameExpression, typeof(T).GetProperty(name));

        // Create expression body based on type of filter
        Expression expression;
        MethodInfo method;
        switch(filterType) {
            case FilterType.Like:
                method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
                expression = Expression.Call(propertyExpression, method, value); 
                break;
            case FilterType.EndsWith:
            case FilterType.StartsWith:
                method = typeof(string).GetMethod(filterType.ToString(), new[] { typeof(string) });
                expression = Expression.Call(propertyExpression, method, value);
                break;
            case FilterType.GreaterThan:
                expression = Expression.GreaterThan(propertyExpression, value);                    
                break;
            case FilterType.Equals:
                expression = Expression.Equal(propertyExpression, value);
                break;
            default:
                throw new ArgumentException("Filter Type could not be determined");
        }            

        // Execute the expression against Query.
        var methodCallExpression = Expression.Call(
            typeof (Queryable),
            "Where",
            new[] { Query.ElementType },
            Query.Expression,
            Expression.Lambda<Func<T, bool>>(expression, new[] { nameExpression }));

        // Filter the current Query data.
        Query = Query.Provider.CreateQuery<T>(methodCallExpression);            

        return this;
    }

То, что здесь происходит, - это значение константы выражения, отлитое от типа предиката. Для ясности этот метод вызывается:

var paramsData = new NameValueCollection { { "CreatedOn", DateTime.Today.ToString() } };
        var model = m_data.ToPagedList(new ViewDataDictionary(), paramsData, 1, 10, null, x => x.LastName)
                          .Filters(Criteria<TrainerProfile>.New(x => x.CreatedOn, FilterType.GreaterThan))
                          .Setup();

Этот код основан на MVC 3, поэтому некоторые из ParamsData [""], которые являются (Request.Params).

Ответ 4

Я точно не знаю, что такое ваш код, но чтобы получить непустую версию Nullable, вызовите его член .Value.

Ответ 5

Это смешно,

Я пробовал оба варианта этого кода:

    System.Nullable<DateTime> now = new System.Nullable<DateTime>();
    now = DateTime.Now;

и

    System.Nullable<DateTime> now;
    now = DateTime.Now;

и оба они работали без ошибок.

Затем я перечитываю ваш вопрос. На самом деле ответ по-прежнему находится в собственности "Значение". Инициализация переменной, если она прекрасна, но если вы выполните:

(now >= DateTime.Now) в запросе Linq вы получите сообщение об ошибке. Это должно быть (now.Value >= DateTime.Now)