Подтвердить что ты не робот

Преобразование AutoMapper из нескольких источников

Скажем, у меня есть два класса моделей:

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

Также есть класс Телефон:

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

Я не могу найти пример для этого сценария. Возможно ли это?

Примечание. Пример не является реальным, только для этого вопроса.

4b9b3361

Ответ 1

Вы не можете напрямую сопоставить многие источники с одним пунктом назначения - вам следует применять карты по одному, как описано в Andrew Whitaker. Итак, вы должны определить все сопоставления:

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

Ответ 2

Для этого можно использовать 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 ));

Если у вас будет больше исходных моделей, вы можете использовать другое представление (List, Dictionary или что-то еще), которое соберет все эти модели вместе как источник.

Вышеприведенный код следует поместить в некоторый файл AutoMapperConfiguration, установить один раз и глобально, а затем использовать, когда это применимо.

AutoMapper по умолчанию поддерживает только один источник данных. Таким образом, нет возможности устанавливать сразу несколько источников (не обертывая их в коллекцию), потому что тогда как мы узнаем, что, если, например, у двух исходных моделей есть свойства с одинаковыми именами?

Существует хоть какое-то обходное решение для этого:

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