Search code examples
c#.net.net-coreexpression-trees

Exception when generating Expression.Assign to set decimal? from decimal


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;
}

Solution

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