The problem here is that the expression library is throwing an exception when given two arguments of mismatched nullability. Here's a simple repro:
Expression<Func<DateTime?>> ex1 = ()=>DateTime.Now;
Expression<Func<DateTime>> ex2 = ()=>DateTime.Now;
var ex3 = Expression.GreaterThan(ex1.Body, ex2.Body);
It is not clear to me whether this is a bug or not; the rules of C# require that in this scenario, the non-nullable operand is converted to nullable, and the lifted-to-nullable form of the comparison is used. However, the expression tree library is not required to follow the rules of C# because of course the expression tree library can be used to represent C# expressions, Python expressions, JScript expressions, VB expressions and so on; it cannot possibly follow all the rules of every possible language.
But regardless, this looks like it might be a bug, so I'll submit it to the expression tree team and see what they say. In the meanwhile, you can easily work around it by defining your own helper method that fixes the operands up. A quick sketch would be:
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<>);
}
However, notice that this does not check that e1 and e2's type differ only in nullability; if you pass in a nullable int and a non-nullable double expression, bad things happen. I leave it as an exercise to implement the better logic that checks whether the two expressions are of type that only differs by nullability.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…