AutoMapper convert from multiple sources

匿名 (未验证) 提交于 2019-12-03 02:13:02

问题:

Let's say I have two model classes:

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

Also have a class Phone:

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

And I want to convert to a PeoplePhoneDto like this:

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

Let's say in my controller I have:

var people = repository.GetPeople(1); var phone = repository.GetPhone(4);  // normally, without automapper I would made return new PeoplePhoneDto(people, phone) ; 

I cannot seem to find any example for this scenario. Is this possible ?

Note: Example is not-real, just for this question.

回答1:

You cannot directly map many sources to single destination - you should apply maps one by one, as described in Andrew Whitaker answer. So, you have to define all mappings:

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

Then create destination object by any of these mappings, and apply other mappings to created object. And this step can be simplified with very simple extension method:

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

Usage is very simple:

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


回答2:

You could use a Tuple for this:

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

In case you would have more source models you can use a different representation (List, Dictionary or something else) that will gather all these models together as a source.

The above code should preferaby be placed in some AutoMapperConfiguration file, set once and globally and then used when applicable.

AutoMapper by default supports only a single data source. So there is no possibility to set directly multiple sources (without wrapping it up in a collection) because then how would we know what in case if for example two source models have properties with the same names?

There is though some workaround to achieve this:

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

And then:

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

But to be quite honest, even though I am using AutoMapper for already a few years I have never had a need to use mapping from multiple sources. In cases when for example I needed multiple business models in my single view model I simply embedded these models within the view model class.

So in your case it would look like this:

public class PeoplePhoneDto {     public People People { get; set; }     public Phone Phone { get; set; } } 


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!