2011-10-10 96 views
3

我對我的視圖模型使用dynamic對象,因爲我發現使用諸如Automapper之類的東西時不必要的開銷,並且發現此方法更靈活輕便。我現在用的助洗劑impromptu-interface這樣的:將靜態類型的對象展開爲動態對象

private dynamic New = Builder.New(); 

private dynamic GetViewModel(Product p) 
{ 
    var viewModel = New.Product(id : p.Id, name : p.Name); 
    viewModel.AdditionalProperty = "some additional data"; 
    return viewModel; 
} 

有幾種情況中,「擴大」的實際對象將是更好然後重新映射所有屬性一個接一個,類似於你將如何使用JavaScript中做jQuery.extend()

private dynamic GetViewModel(Product p) 
{ 
    var viewModel = //create base dynamic object, that has all the members of p. 
    viewModel.AdditionalProperty = "some additional data"; 
    returm viewModel; 
} 

這應該使用ExpandoObject結合反射和通過所有成員迭代可以實現,但想知道是否有一清潔劑/整齊溶液。

+1

我很可能不會僅僅根據性能做出這樣的決定,但您確定'動態'比使用Automapper快嗎? – svick

+0

這個問題並不是關於性能,而是更輕量,更靈活。一次創建的視圖模型不會超過一百個。 –

回答

4

我最終實現這樣的:

public class ExpandedObject : DynamicObject 
{ 
    private readonly IDictionary<string, object> expando = new ExpandoObject(); 

    public ExpandedObject(object o) 
    {    
     foreach (var propertyInfo in o.GetType().GetProperties(BindingFlags.Public|BindingFlags.Instance)) 
     { 
      this.expando[propertyInfo.Name] = Impromptu.InvokeGet(o, propertyInfo.Name); 
     } 
    } 

    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    {    
     return this.expando.TryGetValue(binder.Name, out result); 
    } 

    public override bool TrySetMember(SetMemberBinder binder, object value) 
    { 
     this.expando[binder.Name] = value; 
     return true; 
    } 
} 

和測試:

[TestFixture] 
public class ExpandedObjectTest 
{ 
    [Test] 
    public void Can_add_new_properties_to_expanded_object() 
    { 
     dynamic expanded = new ExpandedObject(new object()); 
     var data = "some additional data"; 
     expanded.data = data; 
     Assert.AreEqual(data, expanded.data); 
    } 

    [Test] 
    public void Copies_existing_properties() 
    {    
     var obj = new { id = 5 };    
     dynamic expanded = new ExpandedObject(obj);    
     Assert.AreEqual(obj.id, expanded.id);    
    } 
} 

這使得利用Impromptu.InvokeGet(),而不是PropertyInfo.GetValue()因爲Impromptu.InvokeGet()使用DLR,因此大約快2.5倍比使用我的測試反射。總的來說,這項工作相當快速,多達10,000個對象的開銷幾乎不存在。

我應該注意,這不會擴展其他ExpandoObject或類似的,但這不應該真的有必要。

1

您可以創建一個將兩個或多個對象的動態對象:

class CombineDynamic : DynamicObject 
{ 
    private readonly object[] m_objects; 

    public CombineDynamic(params object[] objects) 
    { 
     m_objects = objects; 
    } 

    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
     var callSite = CallSite<Func<CallSite, object, object>>.Create(binder); 

     foreach (var o in m_objects) 
     { 
      try 
      { 
       result = callSite.Target(callSite, o); 
       return true; 
      } 
      catch (RuntimeBinderException) 
      {} 
     } 

     return base.TryGetMember(binder, out result); 
    } 

    public override bool TrySetMember(SetMemberBinder binder, object value) 
    { 
     // the binder from argument uses compile time type from call site, 
     // which is object here; because of that, setting of properties that 
     // aren't of type object wouldn't work if we used that binder directly 
     var fixedBinder = Binder.SetMember(
      CSharpBinderFlags.None, binder.Name, typeof(CombineDynamic), 
      new[] 
      { 
       CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), 
       CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) 
      }); 

     var callSite = 
      CallSite<Action<CallSite, object, object>>.Create(fixedBinder); 

     foreach (var o in m_objects) 
     { 
      try 
      { 
       callSite.Target(callSite, o, value); 
       return true; 
      } 
      catch (RuntimeBinderException) 
      {} 
     } 

     return base.TrySetMember(binder, value); 
    } 
} 

而且使用這樣的:

dynamic viewModel = new CombineDynamic(product, new ExpandoObject()); 
viewModel.AdditionalProperty = "additional data"; 

當你或動態設置屬性,它首先試圖做在第一個對象上,然後在第二個對象上,直到它成功。

否則像這樣已經(至少)一個怪異的行爲:如果,例如,Productint物業類型Id,代碼viewModel.Id = "42";會成功。但它會在ExpandoObject上設置屬性。因此,如果您之後嘗試檢索viewModel.Id,則會從product.Id返回未修改的int

+0

這更像一個代理工作,但不完全是我想要的。但它應該指向正確的方向:) –

+0

是的,你可以說它是一個代理。你喜歡什麼? – svick

+0

我寧願它做一個淺拷貝到另一個對象。但使用對公共財產的反思應該很容易實現。 –