web-dev-qa-db-ja.com

あるオブジェクトを別のオブジェクトにマッピングするためのベストプラクティス

私の質問は、最も保守しやすい方法で、あるオブジェクトを別のオブジェクトにマッピングする最良の方法は何ですか。取得するDtoオブジェクトをより正規化するように設定する方法を変更することはできません。そのため、これをオブジェクトの実装にマッピングする方法を作成する必要があります。

以下は、私が何をする必要があるかを示すサンプルコードです。

class Program
{
    static void Main(string[] args)
    {
        var dto = new Dto();

        dto.Items = new object[] { 1.00m, true, "Three" };
        dto.ItemsNames = new[] { "One", "Two", "Three" };            

        var model = GetModel(dto);

        Console.WriteLine("One: {0}", model.One);
        Console.WriteLine("Two: {0}", model.Two);
        Console.WriteLine("Three: {0}", model.Three);
        Console.ReadLine();
    }

    private static Model GetModel(Dto dto)
    {
        var result = new Model();

        result.One = Convert.ToDecimal(dto.Items[Array.IndexOf(dto.ItemsNames, "One")]);
        result.Two = Convert.ToBoolean(dto.Items[Array.IndexOf(dto.ItemsNames, "Two")]);
        result.Three = dto.Items[Array.IndexOf(dto.ItemsNames, "Three")].ToString();

        return result;
    }
}

class Dto
{
    public object[] Items { get; set; }
    public string[] ItemsNames { get; set; }
}

class Model
{
    public decimal One { get; set; }
    public bool Two { get; set; }
    public string Three { get; set; }
}

モデルオブジェクトpropertyInfo、変換後の型、および取り出したい "itemname"を受け取るようなマッパークラスがあれば素晴らしいと思います。このクリーナーを作るための提案はありますか?

ありがとう!

32
Alex

私は AutoMapper を選択します。これは、慣例に基づいて、あるタイプを別のタイプにマッピングできるオープンソースおよび無料のマッピングライブラリです(つまり、同じ名前および同じ/派生/変換可能なタイプのパブリックプロパティを、他の多くの スマートなもの )。非常に使いやすく、次のようなことを実現できます。

Model model = Mapper.Map<Model>(dto);

特定の要件についてはわかりませんが、AutoMapperは カスタム値リゾルバー もサポートしています。これは、特定のマッパーの単一の汎用実装を作成するのに役立ちます。

22
Efran Cobisi

これは、少しのリフレクションを使用する可能性のある一般的な実装です(擬似コード、現在VSを持っていません):

public class DtoMapper<DtoType>
{
    Dictionary<string,PropertyInfo> properties;

    public DtoMapper()
    {
        // Cache property infos
        var t = typeof(DtoType);
        properties = t.GetProperties().ToDictionary(p => p.Name, p => p);
     }

    public DtoType Map(Dto dto)
    {
        var instance = Activator.CreateInstance(typeOf(DtoType));

        foreach(var p in properties)
        {
            p.SetProperty(
                instance, 
                Convert.Type(
                    p.PropertyType, 
                    dto.Items[Array.IndexOf(dto.ItemsNames, p.Name)]);

            return instance;
        }
    }

使用法:

var mapper = new DtoMapper<Model>();
var modelInstance = mapper.Map(dto);

これは、マッパーインスタンスを作成するときは遅くなりますが、後でかなり速くなります。

6
Stefano Altieri
/// <summary>
/// map properties
/// </summary>
/// <param name="sourceObj"></param>
/// <param name="targetObj"></param>
private void MapProp(object sourceObj, object targetObj)
{
    Type T1 = sourceObj.GetType();
    Type T2 = targetObj.GetType();

    PropertyInfo[] sourceProprties = T1.GetProperties(BindingFlags.Instance | BindingFlags.Public);
    PropertyInfo[] targetProprties = T2.GetProperties(BindingFlags.Instance | BindingFlags.Public);

   foreach (var sourceProp in sourceProprties)
   {
       object osourceVal = sourceProp.GetValue(sourceObj, null);
       int entIndex = Array.IndexOf(targetProprties, sourceProp);
       if (entIndex >= 0)
       {
           var targetProp = targetProprties[entIndex];
           targetProp.SetValue(targetObj, osourceVal);
       }
   }
}
3
DKM