3

dynamicAutomapperのようなものを使用することによるオーバーヘッドが不要であり、このアプローチがはるかに柔軟で軽量であることがわかったため、ビューモデルにオブジェクトを使用しています。私は次のような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を使用して行うのと同様に、実際のオブジェクトを「拡張」してから、すべてのプロパティを1つずつ再マッピングする方がよいシナリオがいくつかあります。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";
    return viewModel;
}

これは、リフレクションと組み合わせてすべてのメンバーを反復処理することで達成できるはずExpandoObjectですが、よりクリーンでクリーンなソリューションがあるかどうかを知りたいと思います。

4

2 に答える 2

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

これは、DLRを使用するため、代わりImpromptu.InvokeGet()にを使用します。そのため、私のテストからの反射よりも約2.5倍高速です。全体として、これはかなり高速に動作し、最大10,000個のオブジェクトのオーバーヘッドはほとんど存在しません。PropertyInfo.GetValue()Impromptu.InvokeGet()

これは他のまたは同様のものを拡張するためには機能しないことに注意するExpandoObject必要がありますが、とにかくこれは実際には必要ではないはずです。

于 2011-10-10T22:17:21.593 に答える
1

2つ以上のオブジェクトを組み合わせた動的オブジェクトを作成できます。

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

プロパティを動的に取得または設定すると、最初に最初のオブジェクトでそれを試行し、次に2番目のオブジェクトでそれを実行しようとします。

このようにすると、(少なくとも)1つの奇妙な動作が発生します。たとえば、タイプのProductプロパティがある場合、コードは成功します。ただし、プロパティはに設定されます。したがって、その後取得しようとすると、変更されていないfromが返されます。IdintviewModel.Id = "42";ExpandoObjectviewModel.Idintproduct.Id

于 2011-10-10T14:57:02.350 に答える