2015-10-15 56 views
3

反對我有該噴射器使用表達式分配值使用表達式

/// <summary> 
    /// Inject a data to an instance of T 
    /// </summary> 
    /// <typeparam name="T">The type of object as parameter</typeparam> 
    /// <param name="data">Object instance to where the data is injected</param> 
    /// <param name="pairData">Set of data info</param> 
    public static void InjectData<T>(this T data, Dictionary<Expression<Func<T, object>>, object> pairData) where T : IAuditable 
    { 
     // posible content of T data 
     // Employee 
     //  : Code 
     //  : Name 
     //  : ID 

     // possible content of pairData 
     // Key: a => a.Code 
     // Value: "12345" 

     // Key: a => a.Name 
     // Value: "Vincent" 

     // after the injection, the data (Employee) will now have value on each properties: Code, Name 

     foreach (var _o in pairData) 
     { 

      var _value = Expression.Constant(_o.Value); 
      var _assign = Expression.Assign(_o.Key, _value); 

      //_lambda.Compile()(data); 
      //var _lambda = Expression.Lambda<Action<object, T>>(_assign, _value, _o.Key); 

      //_lambda.Compile()(_o.Value, data); 

     } 
    } 

我只通過表達和值的集合,其注入數據。

表達在其上,其中,以分配的值(例如A => a.Code)和 值(例如,「123456」)

我想將它分配給一個物體T但與我目前的嘗試,我得到了一個System.ArgumentException:爲lambda聲明提供的參數數量不正確。我被卡住了,不知道下一步該怎麼做。 :)任何人都可以指出我如何做這項工作的正確方向?

這是樣品使用

Employee _employee = new Employee(); 

Dictionary<Expression<Func<Employee, object>>, object> _x = new Dictionary 
       <Expression<Func<Employee, object>>, object> 
      { 
       {a => a.Code, "12345"} 
      }; 

_employee.InjectData(_x); 

Assert.AreEqual("12345", _employee.Code); 

我也嘗試

foreach (var _o in pairData) 
     { 
      var _mem = _o.Key.Body as MemberExpression; 
      var _prop = _mem.Member as PropertyInfo; 
      var _setMethod = _prop.GetSetMethod(); 

      _prop.SetValue(data, _o.Value); 

,我可以在這裏看到一個進步,因爲私有財產已經分配但獲取方法爲NULL

Screenshot

在此先感謝

任何幫助,將不勝感激。

+0

示例用法不能編譯。 –

+0

哦,即時對不起, - 更新 –

+0

http://stackoverflow.com/a/8111923/1663001 – DavidG

回答

1

不作太多的感覺給我,但在這裏它是:

public static void InjectData<T>(this T data, Dictionary<Expression<Func<T, object>>, object> pairData) where T : IAuditable 
{ 
    foreach (var item in pairData) 
    { 
     var member = item.Key; 
     // If member type is a reference type, then member.Body is the property accessor. 
     // For value types it is Convert(property accessor) 
     var memberBody = member.Body as MemberExpression ?? (MemberExpression)((UnaryExpression)member.Body).Operand; 
     var lambda = Expression.Lambda<Action<T>>(Expression.Assign(memberBody, Expression.Constant(item.Value)), member.Parameters); 
     var action = lambda.Compile(); 
     action(data); 
    } 
} 

UPDATE正如評論請求,支持嵌套類屬性intitialization版本:

public static void InjectData<T>(this T data, Dictionary<Expression<Func<T, object>>, object> pairData) //where T : IAuditable 
{ 
    var assignments = new List<Expression>(); 
    foreach (var item in pairData) 
    { 
     var member = item.Key; 
     // If member type is a reference type, then member.Body is the property accessor. 
     // For value types it is Convert(property accessor) 
     var memberBody = member.Body as MemberExpression ?? (MemberExpression)((UnaryExpression)member.Body).Operand; 
     assignments.Clear(); 
     assignments.Add(Expression.Assign(memberBody, Expression.Constant(item.Value))); 
     var target = member.Parameters[0]; 
     while (memberBody.Expression != target) 
     { 
      var childMember = (MemberExpression)memberBody.Expression; 
      assignments.Add(Expression.IfThen(Expression.ReferenceEqual(childMember, Expression.Constant(null)), 
       Expression.Assign(childMember, Expression.New(childMember.Type)))); 
      memberBody = childMember; 
     } 
     assignments.Reverse(); 
     var body = assignments.Count > 1 ? Expression.Block(assignments) : assignments[0]; 
     var lambda = Expression.Lambda<Action<T>>(body, target); 
     var action = lambda.Compile(); 
     action(data); 
    } 
} 
+0

嗨伊萬,謝謝你的迴應,它似乎在這裏工作,我唯一的問題是,當它是一個嵌套的屬性。 ** a => a.Employment.Code ** ..我知道拋出對象引用未設置爲對象的實例,因爲就業未實例化。它可能會自動實例化嗎? –

+1

@EntengDagpin可以完成,但不像上面的代碼片段那麼簡單。 –

+0

我要開始我的解決方案這個片段..謝謝@ivan –

2

可以使用以下代碼:

public static void InjectData<T>(this T data, Dictionary<Expression<Func<T, object>>, object> pairData) 
{ 
    foreach (var pair in pairData) 
    { 
     data.SetPropertyValue(pair.Key, pair.Value); 
    } 
} 

public static T SetPropertyValue<T>(this T target, Expression<Func<T, object>> memberLamda, object value) 
{ 
    var memberSelectorExpression = memberLamda.Body as MemberExpression; 
    if (memberSelectorExpression == null) 
    { 
     return target; 
    } 

    var property = memberSelectorExpression.Member as PropertyInfo; 
    if (property == null) 
    { 
     return target; 
    } 

    property.SetValue(target, value, null); 

    return target; 
} 

基本上,這增加了另一個擴展方法來設置基於該表達式的屬性。

如果你只想要做的改變當值實際上是不同的(與INotifyPropertyChanged工作時,這是很方便的),你可以使用下面的代碼需要一個額外的比較器進行檢查,看看它的值改爲:

public static T SetPropertyValue<T, R>(this T target, Expression<Func<T, R>> memberLamda, R value) 
{ 
    return target.SetPropertyValue(memberLamda, value, EqualityComparer<R>.Default); 
} 

public static T SetPropertyValue<T, R>(this T target, Expression<Func<T, R>> memberLamda, R value, IEqualityComparer<R> comparer) 
{ 
    var memberSelectorExpression = memberLamda.Body as MemberExpression; 
    if (memberSelectorExpression == null) 
    { 
     return target; 
    } 

    var property = memberSelectorExpression.Member as PropertyInfo; 
    if (property == null) 
    { 
     return target; 
    } 

    var currentValue = (R) property.GetValue(target, null); 

    if (comparer.Equals(currentValue, value)) 
    { 
     return target; 
    } 

    property.SetValue(target, value, null); 

    return target; 
} 

請注意,這裏我們使用比較器來檢查當前值是否等於新值。如果是這樣,我們只是返回(不需要更新),如果不是,我們設置值。