2011-03-16 73 views
7

我想創建一個只知道字段名稱的MemberExpression;例如: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; 
    } 

上面的問題是,字段類型必須是強類型。傳遞「對象」作爲字段類型不起作用。有什麼辦法可以產生這個?即使動態LINQ似乎不起作用。

回答

16

有許多與你的代碼問題:

  1. 你的方法的參數被稱爲fieldName,但你得到一個財產說出這樣的話。
  2. 您正在使用非通用Expression.Lambda方法生成表達式,如果傳遞給該方法的類型參數T與屬性類型不同,該表達式可能會選擇不適當的委託類型。在這種情況下,從表達式轉換到方法的返回類型的as將失敗並計算爲null。解決方案:使用帶有適當類型參數的genericLambda方法。不需要鑄造。
  3. 如果解決了第二個問題,當從屬性類型到T之間提供安全的參考轉換時,事情就會正常工作,但在需要更復雜的轉換(例如裝箱/提升)時無法正常工作。解決方案:必要時使用Expression.Convert方法。

下面是一個更新你的樣品,解決了這些問題:

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

這將使所有下列呼叫的成功:

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

謝謝@Ani。幫助了我很多。對於我自己的代碼,我還添加了一個採用propertyInfo而不是propertyName的方法。 – Mithon 2013-03-14 09:43:59

+0

非常感謝!你的代碼示例幫助我理解了表達式的構建,並允許我做我想做的事:D – Shautieh 2013-07-02 13:28:29

2

嘗試在傳遞「對象」的情況下手動轉換字段值。例如:

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

希望這會對你有幫助。

+1

這個答案也是正確的。 – johnnyboy 2011-03-21 00:09:29