Dynamic MemberExpression

c# dynamic expression-trees linq

Question

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.

Accepted Answer

There are a number of issues with your code:

  1. The parameter to your method is called fieldName, but you are getting a property out with it.
  2. 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.
  3. 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");

Popular Answer

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.




Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why