web-dev-qa-db-ja.com

AutoMapperは複数のソースから変換します

私には2つのモデルクラスがあるとしましょう:

public class People {
   public string FirstName {get;set;}
   public string LastName {get;set;}
}

クラスPhoneもあります。

public class Phone {
   public string Number {get;set;}
}

そして、私はこのようなPeoplePhoneDtoに変換したい:

public class PeoplePhoneDto {
    public string FirstName {get;set;}
    public string LastName {get;set;}
    public string PhoneNumber {get;set;}
}

私のコントローラに私が持っているとしましょう:

var people = repository.GetPeople(1);
var phone = repository.GetPhone(4);

// normally, without automapper I would made
return new PeoplePhoneDto(people, phone) ;

このシナリオの例は見当たりません。これは可能ですか?

注:この質問のためだけに、例は現実的ではありません。

57
Bart Calixto

多くのソースを単一の宛先に直接マップすることはできません- Andrew Whitaker answerで説明されているように、マップを1つずつ適用する必要があります。したがって、すべてのマッピングを定義する必要があります。

Mapper.CreateMap<People, PeoplePhoneDto>();
Mapper.CreateMap<Phone, PeoplePhoneDto>()
        .ForMember(d => d.PhoneNumber, a => a.MapFrom(s => s.Number));

次に、これらのマッピングのいずれかによって宛先オブジェクトを作成し、作成したオブジェクトに他のマッピングを適用します。そして、このステップは非常に簡単な拡張方法で簡素化できます:

public static TDestination Map<TSource, TDestination>(
    this TDestination destination, TSource source)
{
    return Mapper.Map(source, destination);
}

使い方はとても簡単です:

var dto = Mapper.Map<PeoplePhoneDto>(people)
                .Map(phone);
87

これにはTupleを使用できます。

Mapper.CreateMap<Tuple<People, Phone>, PeoplePhoneDto>()
    .ForMember(d => d.FirstName, opt => opt.MapFrom(s => s.Item1.FirstName))
    .ForMember(d => d.LastName, opt => opt.MapFrom(s => s.Item1.LastName))
    .ForMember(d => d.Number, opt => opt.MapFrom(s => s.Item2.Number ));

より多くのソースモデルがある場合は、これらのすべてのモデルをソースとして収集する別の表現(リスト、ディクショナリなど)を使用できます。

上記のコードは、AutoMapperConfigurationファイルに配置し、一度だけグローバルに設定し、必要に応じて使用することをお勧めします。

AutoMapperはデフォルトで単一のデータソースのみをサポートします。そのため、たとえば2つのソースモデルに同じ名前のプロパティがある場合、どのようにすればそれを知ることができるのでしょうか?

ただし、これを実現するにはいくつかの回避策があります。

public static class EntityMapper
{
    public static T Map<T>(params object[] sources) where T : class
    {
        if (!sources.Any())
        {
            return default(T);
        }

        var initialSource = sources[0];

        var mappingResult = Map<T>(initialSource);

        // Now map the remaining source objects
        if (sources.Count() > 1)
        {
            Map(mappingResult, sources.Skip(1).ToArray());
        }

        return mappingResult;
    }

    private static void Map(object destination, params object[] sources)
    {
        if (!sources.Any())
        {
            return;
        }

        var destinationType = destination.GetType();

        foreach (var source in sources)
        {
            var sourceType = source.GetType();
            Mapper.Map(source, destination, sourceType, destinationType);
        }
    }

    private static T Map<T>(object source) where T : class
    {
        var destinationType = typeof(T);
        var sourceType = source.GetType();

        var mappingResult = Mapper.Map(source, sourceType, destinationType);

        return mappingResult as T;
    }
}

その後:

var peoplePhoneDto = EntityMapper.Map<PeoplePhoneDto>(people, phone);

しかし、正直に言うと、既に数年間AutoMapperを使用していますが、複数のソースからのマッピングを使用する必要はありませんでした。たとえば、単一のビューモデルに複数のビジネスモデルが必要な場合は、これらのモデルをビューモデルクラスに単純に埋め込みました。

したがって、あなたの場合は次のようになります。

public class PeoplePhoneDto {
    public People People { get; set; }
    public Phone Phone { get; set; }
}
17
Paweł Bejger