I am wanting to create a MemberExpression knowing only the field name; eg:
public static Expression<Func<TModel, T>> GenerateMemberExpression<TModel, T>(string fieldName)
{
PropertyInfo fieldPropertyInfo;
fieldPropertyInfo = typeof(TModel).GetProperty(fieldName);
var entityParam = Expression.Parameter(typeof(TModel), "e"); // {e}
var columnExpr = Expression.MakeMemberAccess(entityParam, fieldPropertyInfo); // {e.fieldName}
var lambda = Expression.Lambda(columnExpr, entityParam) as Expression<Func<TModel, T>>; // {e => e.column}
return lambda;
}
The problem with the above is that the field type must be strongly typed. Passing "object" in as the field type doesn't work. Is there any way to generate this? Even Dynamic LINQ doesn't appear to work.
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");
Try manually converting the field value in case of passing an "object". For example:
var columnExpr = Expression.MakeMemberAccess(entityParam, fieldPropertyInfo); // {e.fieldName}
if (T.GetType().Equals(typeof(object)))
{
columnExpr = Expression.Convert(columnExpr, typeof(object));
}
Hope this will help you.