There are a number of issues with your code:
- The parameter to your method is called
fieldName
, but you are getting a property out with it.
- You are using the non-generic
Expression.Lambda
method to generate the expression, which may choose an inappropriate delegate-type if the type-argument T
passed to the method is not the same as the property-type. In this case, the as
cast from the expression to the method's return-type will fail and evaluate to null
. Solution: Use the generic Lambda
method with the appropriate type-arguments. No casting required.
- If you solve the second issue, things will work fine when a safe reference-conversion is available from the property-type to
T
, but not when more complicated conversions such as boxing / lifting are required. Solution: Use the Expression.Convert
method where necessary.
Here's an update to your sample that addresses these issues:
public static Expression<Func<TModel, T>> GenerateMemberExpression<TModel, T>
(string propertyName)
{
var propertyInfo = typeof(TModel).GetProperty(propertyName);
var entityParam = Expression.Parameter(typeof(TModel), "e");
Expression columnExpr = Expression.Property(entityParam, propertyInfo);
if (propertyInfo.PropertyType != typeof(T))
columnExpr = Expression.Convert(columnExpr, typeof(T));
return Expression.Lambda<Func<TModel, T>>(columnExpr, entityParam);
}
This will make all of the following calls succeed:
GenerateMemberExpression<FileInfo, string>("Name");
GenerateMemberExpression<string, int>("Length");
// Reference conversion
GenerateMemberExpression<FileInfo, object>("Name");
//Boxing conversion
GenerateMemberExpression<string, object>("Length");
//Lifted conversion
GenerateMemberExpression<string, int?>("Length");