I'm trying to convert a Parameter expression and having trouble with converting to value types. Below is a sample of my code:
public static MemberExpression ConvertToType(ParameterExpression sourceParameter,
PropertyInfo propertyInfo,
TypeCode typeCode)
{
var sourceExpressionProperty = Expression.Property(sourceParameter, sourceProperty);
//throws an exception if typeCode is a value type.
Expression convertedSource = Expression.Convert(sourceExpressionProperty,
Type.GetType("System." + typeCode));
return convertedSource;
}
I get the following invalid operation exception:
No coercion operator is defined between types 'System.String' and 'System.Decimal'.
Any help with this conversion would be greatly appreciated.
public class ExpressionUtils
{
public static MethodCallExpression ConvertToType(
ParameterExpression sourceParameter,
PropertyInfo sourceProperty,
TypeCode typeCode)
{
var sourceExpressionProperty = Expression.Property(sourceParameter, sourceProperty);
var changeTypeMethod = typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(TypeCode) });
var callExpressionReturningObject = Expression.Call(changeTypeMethod, sourceExpressionProperty, Expression.Constant(typeCode));
return callExpressionReturningObject;
}
}
Note that the resulting expression is a call to Convert.ChangeType method, which will return System.Object.
Here is a unit test:
[TestClass]
public class UnitTest1
{
private class MyClass
{
public string ValueAsString { get; set; }
}
[TestMethod]
public void TestMethod1()
{
var parameter = Expression.Parameter(typeof(MyClass));
var property = typeof(MyClass).GetProperty("ValueAsString");
var lambdaBody = ExpressionUtils.ConvertToType(parameter, property, TypeCode.Decimal);
var lambda = Expression.Lambda<Func<MyClass, object>>(lambdaBody, parameter);
var valueAsDecimal = (decimal) lambda.Compile().Invoke(new MyClass { ValueAsString = "42" });
Assert.AreEqual(42m, valueAsDecimal);
}
}
The solution I went with was:
private static Expression GetConvertedSource(ParameterExpression sourceParameter,
PropertyInfo sourceProperty,
TypeCode typeCode)
{
var sourceExpressionProperty = Expression.Property(sourceParameter,
sourceProperty);
var changeTypeCall = Expression.Call(typeof(Convert).GetMethod("ChangeType",
new[] { typeof(object),
typeof(TypeCode) }),
sourceExpressionProperty,
Expression.Constant(typeCode)
);
Expression convert = Expression.Convert(changeTypeCall,
Type.GetType("System." + typeCode));
var convertExpr = Expression.Condition(Expression.Equal(sourceExpressionProperty,
Expression.Constant(null, sourceProperty.PropertyType)),
Expression.Default(Type.GetType("System." + typeCode)),
convert);
return convertExpr;
}
Note the Expression.Condition
to handle nulls.