2011-03-16 15 views
7

Quiero crear MemberExpression solo con el nombre del campo; por ejemplo:Dynamic MemberExpression

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; 
    } 

El problema con lo anterior es que el tipo de campo debe estar fuertemente tipado. Pasar "objeto" en como el tipo de campo no funciona. ¿Hay alguna forma de generar esto? Incluso Dynamic LINQ no parece funcionar.

Respuesta

16

Hay una serie de problemas con su código:

  1. El parámetro a su método se llama fieldName, pero que está recibiendo un propiedad a cabo con él.
  2. Está utilizando el método no genérico Expression.Lambda para generar la expresión, que puede elegir un tipo de delegado inapropiado si el argumento de tipo T pasado al método no es el mismo que el tipo de propiedad. En este caso, la conversión as de la expresión al tipo de devolución del método fallará y se evaluará a null. Solución: utilice el método genericLambda con los argumentos de tipo apropiados. No se requiere lanzamiento.
  3. Si resuelve el segundo problema, las cosas funcionarán correctamente cuando se disponga de una conversión de referencia segura del tipo de propiedad a T, pero no cuando se requieran conversiones más complicadas como boxeo/elevación. Solución: utilice el método Expression.Convert cuando sea necesario.

Aquí es una actualización de la muestra que se ocupa de estas cuestiones:

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); 
} 

Esto hará que todas las siguientes llamadas de éxito:

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"); 
+2

Gracias @Ani. Me ayudó bastante. Para mi propio código, también agregué un método que tomaba propertyInfo en lugar de propertyName. – Mithon

+0

¡Muchas gracias! Su muestra de código me ayudó a entender la construcción de expresiones y me permitió hacer lo que quería: D – Shautieh

2

Intente convertir manualmente el valor del campo en caso de pasar un "objeto". Por ejemplo:

var columnExpr = Expression.MakeMemberAccess(entityParam, fieldPropertyInfo); // {e.fieldName} 
if (T.GetType().Equals(typeof(object))) 
{ 
    columnExpr = Expression.Convert(columnExpr, typeof(object)); 
} 

Espero que esto te ayude.

+1

Esta respuesta también es correcta. – johnnyboy