'Expression.GreaterThan fails if one operand is nullable type, other is non-nullable

I am creating some dynamic linq and am having problems with the following exception:

The binary operator GreaterThanOrEqual is not defined for the types 'System.Nullable`1[System.DateTime]' and 'System.DateTime'

I get why, because my field type is nullable and Im passing in DateTime.Now essentially.

So in trying to resolve this issue I've tried

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

But the resulting type is a non-nullable object and hence still giving me the above exception.

Any suggestions?!

Update: For more clarification the now variable becomes a non-nullable type when it is set rather than staying as a nullable DateTime so the match throws an exception

Update: The actual code can be seen in the CodePlex project:

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

The offending line is ~145

fExp = Expression.GreaterThanOrEqual(fExpLeft, fExpRight);


Solution 1:[1]

I'm not sure exactly what your code is, but to get the non-nullable version of a Nullable, call its .Value member.

Solution 2:[2]

where ever your compare is, change the compare like this:

(nullableDT >= DT)

To

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

Edit:

As per you comment, Write a function that takes 2 objects, inside the function check if they are nullable types, and check for null, then compare values. This function will probably use code similar to ^.

Though, this is starting to sound like you have a bigger underlying problem. Either you are getting incorrect data, (ie. your code elsewhere is returning data that it shouldn't be, not a problem in the code, but a problem in your logic,) or your assumption that you can compare these objects is invalid. Which, once again, is a logic error.

I think you might need to take a step back on this one for a minute. If you post more code, we might be able to help you some more.

Solution 3:[3]

I found a solution that works within the .Net framework. Here a method that's still in progress but it does work, and works for Linq to 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;
    }

What happens here is the Expression Constant value is cast to the predicate type. For clarity this method is called by:

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();

This code is MVC 3 based, hence some of the ParamsData[""] which is (Request.Params).

Solution 4:[4]

That is funny,

I've tried both variation of this code:

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

and

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

and both of them worked without errors.

Then I re-read your question. Actually the answer is still on the "Value" property. Initializing the variable if fine, but if you do:

(now >= DateTime.Now) in the Linq query you will get an error. It should be (now.Value >= DateTime.Now)

Solution 5:[5]

This is pretty easy and doesn't need a long explaination as from what I have seen. Everything beyond this is uneccessary.

Expression.Constant() takes a Type parameter.

So for example, with ints to int?. Instead of Expression.Constant(someInt) use Expression.Constant(someInt, typeof(int?)) This will cast someInt to int? before executing.

Feel free to use generics and/or reflection for your types if need be.

This would be full code to create a GreaterThan expression using generics and reflection. Using this, it doesn't matter if it is nullable or not

var greaterThan = Expression.GreaterThan(
        Expression.Property(Expression.Parameter(typeof(T)), property),
        Expression.Constant(inValue, property.PropertyType));

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 John Feminella
Solution 2
Solution 3 Novak
Solution 4
Solution 5 JSON