2011-06-07 9 views
6

Hola,expresión LINQ miembro de obtener el nombre de columna

estoy utilizando LINQ y EF con C# 4.0. He arrastrado la tabla ELMAH básica a EF (construida y guardada muchas veces). Todo está funcionando como uno esperaría.

Pero he tratado de ser demasiado ambicioso y necesito un poco de ayuda. Estoy tratando de obtener el nombre de la Columna de una expresión que se transmite como una variable.

Lo que yo quiero es éste:

Pass: X => x.ErrorId

y obtener: "ErrorId"

public void GetColumnName(Expression<Func<T, object>> property) 
{ 
    // The parameter passed in x=>x.Message 
    // Message works fine (probably because its a simple string) using: 
    string columnName = (property.Body as MemberExpression).Member.Name; 

    // But if I attempt to use the Guid or the date field then it 
    // is passed in as x => Convert(x.TimeUtc) 
    // As a result the above code generates a NullReference exception 
    // i.e. {"Object reference not set to an instance of an object."} 

    // What is the correct code here to extract the column name generically? 
    // Ideally in a way that won't bite me again in the future. 

} 

Gracias por su ayuda! Dan.

+0

Así que lo que busca es determinar el nombre de columna en una expresión potencialmente más complicado que simplemente 'x. ColumnName'? – user7116

+0

intente la depuración y ponga 'property.Body como MemberExpression' en el reloj, una vez que golpee el' ErrorId' puede ver cómo extraerlo – oleksii

+0

@sixlettervariables correcta. –

Respuesta

6

Si también necesita descomponer expresiones simples (o casi simples), necesitará un poco de trabajo adicional para manejar las diferentes situaciones. Aquí hay un código de arranque que maneja algunos casos comunes:

string GetColumnName<T,TResult>(Expression<Func<T,TResult>> property) 
{ 
    var member = GetMemberExpression(property.Body); 
    if (member == null) 
     throw new ArgumentException("Not reducible to a Member Access", 
            "property"); 

    return member.Member.Name; 
} 

MemberExpression GetMemberExpression(Expression body) 
{ 
    var candidates = new Queue<Expression>(); 
    candidates.Enqueue(body); 
    while (candidates.Count > 0) 
    { 
     var expr = candidates.Dequeue(); 
     if (expr is MemberExpression) 
     { 
      return ((MemberExpression)expr); 
     } 
     else if (expr is UnaryExpression) 
     { 
      candidates.Enqueue(((UnaryExpression)expr).Operand); 
     } 
     else if (expr is BinaryExpression) 
     { 
      var binary = expr as BinaryExpression; 
      candidates.Enqueue(binary.Left); 
      candidates.Enqueue(binary.Right); 
     } 
     else if (expr is MethodCallExpression) 
     { 
      var method = expr as MethodCallExpression; 
      foreach (var argument in method.Arguments) 
      { 
       candidates.Enqueue(argument); 
      } 
     } 
     else if (expr is LambdaExpression) 
     { 
      candidates.Enqueue(((LambdaExpression)expr).Body); 
     } 
    } 

    return null; 
} 

que produce una salida como:

GetColumnName((x) => x.X): "X" 
GetColumnName((x) => x.X + 2): "X" 
GetColumnName((x) => 2 + x.X): "X" 
GetColumnName((x) => -x.X): "X" 
GetColumnName((x) => Math.Sqrt(x.Y)): "Y" 
GetColumnName((x) => Math.Sqrt(Math.Abs(x.Y))): "Y" 
+0

utilizando casi exactamente el mismo código, especialmente para corregir esta declaración de inclusión tonto en EF, que es extremadamente difícil de refactorizar debido a las cadenas pasadas como params. – vittore

+0

¡Excelente respuesta! Funciona de maravilla. ¿Hay alguna limitación que deba tener en cuenta? Solo para que sepa que tengo un HTMLHelper en MVC que toma estas expresiones lambda para crear una lista personalizada de campos para mostrar (anulando el valor predeterminado), por lo que debería ser excelente para lo que necesito. ¡Gracias! –

+0

@Dan: si tiene más de una propiedad allí, devolverá la más a la izquierda. Además, devolverá el acceso de miembro de * cualquier * más a la izquierda. Esto significa que algunas expresiones complicadas como '(x) => y.PropA + x.PropB' reportarán' PropA'. – user7116

Cuestiones relacionadas