I ran into an interesting issue when building Expressions. I had some basic type coercion checks going to ensure minimal casting was done, however, I ran into an issue I didn't expect.
When I am trying to generate a BinaryExpression
using Expression.Assign
and I am going from decimal
to decimal?
I receive the exception:
System.ArgumentException: 'Expression of type 'System.Decimal' cannot be used for assignment to type 'System.Nullable'1[System.Decimal]
Can someone explain this? Considering the following evaluates to true:
typeof(decimal?).IsAssignableFrom(typeof(decimal))
The expected assignment should be equal to the statements below:
decimal? x = null;
decimal y = 10;
x = y;
Code in question:
private Expression BuildMapExpressionForValueMap(MemberInfo destinationProperty, MemberInfo sourceProperty)
{
Expression assignmentExpression = Expression.PropertyOrField(_source, sourceProperty.Name);
Type destinationType = destinationProperty.GetUnderlyingType();
if (!destinationType.IsAssignableFrom(sourceProperty.GetUnderlyingType()))
{
assignmentExpression = BuildCastExpression(assignmentExpression, destinationType);
}
var expression = Expression.Assign(Expression.PropertyOrField(_destination, destinationProperty.Name)
, assignmentExpression);
return expression;
}
There are implicit conversions from the non-nullable value types to the corresponding nullable type. The expressions you generate must be explicit. The same reason why you can't generate an expression assigning an Int32 to a variable of type Int64. The compiler generates the conversion calls explicitly so you don't have to. Try it and you'll see.
You must add the conversion.
var param = Expression.Variable(typeof(decimal?));
var value = Expression.Constant(20m, typeof(decimal));
var expr = Expression.Assign(param,
//value // fails
Expression.Convert(value, param.Type)
);