I have an expression tree function from a previous SO question. It basically allows the conversion of a data row into a specific class.
This code works fine, unless you’re dealing with data types that can be bigger or smaller (eg. Int32/Int64).
The code throws an invalid cast exception when going from an Int64 to an Int32 when the value would fit in an Int32 (eg. numbers in the 3000).
Should I?
- Attempt to fix this in the code? (If so, any pointers?)
-
Leave the code as it is.
private Func<SqlDataReader, T> getExpressionDelegate<T>() { // hang on to row[string] property var indexerProperty = typeof(SqlDataReader).GetProperty("Item", new[] { typeof(string) }); // list of statements in our dynamic method var statements = new List<Expression>(); // store instance for setting of properties ParameterExpression instanceParameter = Expression.Variable(typeof(T)); ParameterExpression sqlDataReaderParameter = Expression.Parameter(typeof(SqlDataReader)); // create and assign new T to variable: var instance = new T(); BinaryExpression createInstance = Expression.Assign(instanceParameter, Expression.New(typeof(T))); statements.Add(createInstance); foreach (var property in typeof(T).GetProperties()) { // instance.MyProperty MemberExpression getProperty = Expression.Property(instanceParameter, property); // row[property] -- NOTE: this assumes column names are the same as PropertyInfo names on T IndexExpression readValue = Expression.MakeIndex(sqlDataReaderParameter, indexerProperty, new[] { Expression.Constant(property.Name) }); // instance.MyProperty = row[property] BinaryExpression assignProperty = Expression.Assign(getProperty, Expression.Convert(readValue, property.PropertyType)); statements.Add(assignProperty); } var returnStatement = instanceParameter; statements.Add(returnStatement); var body = Expression.Block(instanceParameter.Type, new[] { instanceParameter }, statements.ToArray()); var lambda = Expression.Lambda<Func<SqlDataReader, T>>(body, sqlDataReaderParameter); // cache me! return lambda.Compile(); }
Update:
I have now given up and decided it is not worth it. From the comments below, I got as far as:
if (readValue.Type != property.PropertyType)
{
BinaryExpression assignProperty = Expression.Assign(getProperty, Expression.Convert(Expression.Call(property.PropertyType, "Parse", null, new Expression[] { Expression.ConvertChecked(readValue, typeof(string)) }), property.PropertyType));
statements.Add(assignProperty);
}
else
{
// instance.MyProperty = row[property]
BinaryExpression assignProperty = Expression.Assign(getProperty, Expression.Convert(readValue, property.PropertyType));
statements.Add(assignProperty);
}
I don’t think I was too far off, feel free to finish it and post the answer if you figure it out 🙂
You could try to fix it by “convert checked” before assigning i.e. using
Expression.ConvertCheckedon the value instead ofExpression.Convert.Couldn’t try it right now but this should take care of the case you describe…
EDIT – as per comment this could be a boxing issue:
In this case you could try using
Expression.TypeAsorExpression.Unboxfor the conversion or useExpression.Callfor calling a method to do the conversion… an example for usingCallcan be found at http://msdn.microsoft.com/en-us/library/bb349020.aspx