2011-06-07 80 views
6

你好,LINQ成員表達越來越列名

我使用LINQ和EF與C#4.0。 我已經將基本的ELMAH表拖入EF(構建並保存很多次)。 所有工作正如人們所期望的那樣。

但試圖過於雄心勃勃,需要一些幫助 - 我想從傳遞中作爲一個變量表達式得到列名。

我想是這樣的:

通在:X => x.ErrorId

,並得到: 「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. 

} 

謝謝您的幫助! Dan。

+0

所以,你希望從一個潛在的更加複雜的表達式確定列名不僅僅是'X。 ColumnName'? – user7116 2011-06-07 18:26:39

+0

嘗試調試,並把'property.Body作爲MemberExpression'融入腕錶,一旦你打'ErrorId'你可能會看到如何提取它 – oleksii 2011-06-07 18:39:18

+0

@sixlettervariables正確。 – 2011-06-07 19:23:44

回答

6

如果您還需要分解簡單(或幾乎簡單)的表達式,你需要一些額外的跑腿來處理不同的情況。這裏是處理一些常見的情況下,某些啓動代碼:哪些生產同類產品的輸出

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

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

使用幾乎完全同樣的代碼,尤其是解決這個傻包括在EF語句,它堅硬無比,因爲作爲PARAMS傳遞的字符串的重構。 – vittore 2011-06-07 19:06:06

+0

絕妙的回答!奇蹟般有效。我應該知道有什麼限制嗎?只要你知道我有一個MVC的HtmlHelper這需要這些lambda表達式建立字段的自定義列表顯示(overridding默認值),所以應該是超我需要什麼。謝謝! – 2011-06-07 19:32:00

+0

@Dan:如果你有多個屬性,它將返回最左邊的屬性。此外,它將返回*任何*成員訪問的最左側。這意味着一些複雜的表達式如'(x)=> y.PropA + x.PropB'會報告'PropA'。 – user7116 2011-06-07 19:37:08