2016-10-26 11 views
1

反思親愛的神泛型方法來獲得屬性值的LINQ表達和思考

我想有一個通用的GetValue<TEntity, T>方法,可以返回給下面User類以下屬性值:

public class User 
{ 
    public int Id { get; set; } 
    public int ClientId { get; set; } 
    public string UserName { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string MobileNumber { get; set; } 
    public bool IsActive { get; set; } 

    public Client Client { get; set; } 
    public List<Package> Packages { get; set; } 

} 

var firstName = dataCollector.GetValue<User, string>(x => x.FirstName); 
    var client = dataCollector.GetValue<User, Client>(x => x.Client); 
    var packages = dataCollector.GetValue<User, List<Package>>(x => x.Packages); 

    var packageFirst = dataCollector.GetValue<User, Package>(x => x.Packages[0]); 
    var packageName = dataCollector.GetValue<User, string>(x => x.Packages[0].Name); 
    var clientName = dataCollector.GetValue<User, string>(x => x.Client.Name); 

到目前爲止,我有以下的實現方法具:什麼GetValue<TEntity, T>應該能夠做到

用法示例d其中工程前3個場景:

public T GetValue<TEntity, T>(Expression<Func<TEntity, T>> propertyExpression) where TEntity : class 
{ 
    var response = _responses.FirstOrDefault(p => p.GetType() == typeof(TEntity)) as TEntity; 
    if (response != null) 
    { 
     var expr = (MemberExpression)propertyExpression.Body; 
     var prop = (PropertyInfo)expr.Member; 
     return (T)prop.GetValue(response); 
    } 
    return default(T); 
    } 

但它並不在過去的3種情景工作:

var packageFirst = dataCollector.GetValue<User, Package>(x => x.Packages[0]); 

拋出:Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpressionN' to type 'System.Linq.Expressions.MemberExpression'.

var packageName = dataCollector.GetValue<User, string>(x => x.Packages[0].Name); 

拋出:Object does not match target type.

var clientName = dataCollector.GetValue<User, string>(x => x.Client.Name); 

拋出:Object does not match target type.

我需要對該方法做出哪些更改?

我現在要犧牲一個USB閃存驅動器,而等待你的答案:)

+0

什麼是「不工作」是什麼意思?不編譯?錯誤信息?如果'x.Packages'爲空或null,或者'x.Client'爲null,則會出現運行時錯誤。 –

+1

我們需要更多的答案:誰是dataCollector?什麼樣的?你想在哪裏得到這個?我認爲你是過於複雜的事情。 – Lidaranis

+0

@DStanley它運行但最後3拋出錯誤。看到我編輯的答案 –

回答

3

的問題是在這裏:

if (response != null) 
{ 
    var expr = (MemberExpression)propertyExpression.Body; 
    var prop = (PropertyInfo)expr.Member; 
    return (T)prop.GetValue(response); 
} 

如果僅僅作品的表達引用屬性直接,否則propertyExpression.Body將不會是MemberExpression,您將得到運行時轉換錯誤。不起作用的三個不直接引用一個屬性 - 前兩個屬性頂部的方法(索引器),最後一個引用嵌套屬性。

因爲所有你想要的是該表達式的值,不過,我認爲你可以這樣做:

if (response != null) 
{ 
    Func<TEntity, T> func = propertyExpression.Compile(); 
    return func(response); 
} 

如果你打算做其他事情表達(如獲得名稱),那麼您需要決定是否要支持不直接引用屬性的表達式併爲其添加處理程序。

+0

是的,我似乎是這個問題。將嘗試你的建議。我只需要屬性值 –

+0

太棒了!這樣可行!我猜Compile()在背景中有相當的魔力。我想我需要一些遞歸來獲得嵌套的屬性 –

+1

你可以刪除Linq.Expression和Compile(),只是做:'公共T GetValue (函數函數)其中TEntity:類 { var響應= _responses.FirstOrDefault (p => p.GetType()== typeof(TEntity))作爲TEntity; if(response!= null) return function(response); return default(T); }' –

1

你可以簡單地通過執行你的Lambda表達式做到這一點:

 if (response != null) 
     { 
      return propertyExpression.Compile().Invoke(response); 
     } 
+0

返回'(T)propertyExpression.Compile()。DynamicInvoke(response);'''func(response)''有什麼區別嗎? –

+0

我編輯了我的答案,現在恕我直言,沒有什麼不同:因爲類型是已知的,沒有敏感使用DynamicInvoke至極gona使用反射 – Yahya