2016-05-06 17 views
12

Potrzebuję odwzorować obiekt na inny przy użyciu programu AutoMapper. Trudne pytanie brzmi: w jaki sposób uzyskać dostęp do instancji narzędzia odwzorowującego (instancji programu IMapper) w konfiguracji odwzorowania lub wewnątrz niestandardowego konwertera typów?Jak korzystać z mappera. Mapowanie w MapperConfiguration of AutoMapper?

Poniższy kod nie działa, ale jest przykładem tego, co chciałbym osiągnąć - proszę zwrócić uwagę na wywołania mapper.Map i założyć, że zdefiniowano odwzorowania Customer => CustomerDto i Customer => DetailedCustomerDto.

var config = new MapperConfiguration(
    cfg => cfg.CreateMap<Order, OrderDto>() 
     .ForMember(dst => dst.Customer, src => src.ResolveUsing(o => { 
      return o.Type == 1 
       ? mapper.Map<Customer, CustomerDto>(o.Customer) 
       : mapper.Map<Customer, DetailedCustomerDto>(o.Customer) 
      }) 
    ); 

Część klient jest:

var mapper = config.CreateMapper(); 
var orderDto = mapper.Map<Order, OrderDto>(order); 

Uproszczona wersja obiektów chcę mapie jest:

public class Order 
{ 
    public int Type { get; set; } 
    public Customer Customer { get; set; } 
} 

public class Customer 
{ 
    public long Id { get; set; } 
    public string Name { get; set; } 
} 

public class OrderDto 
{ 
    public CustomerDto Customer { get; set; } 
} 

public class CustomerDto 
{ 
    public long Id { get; set; } 
} 

public class DetailedCustomerDto : CustomerDto 
{ 
    public string Name { get; set; } 
} 

Jak widać z kodów powyżej, na podstawie wartości Order.Type, mapper powinien odwzorować obiekt Order.Customer na różne cele. Ponieważ jeden cel (DetailedCustomerDto) dziedziczy po drugim (CustomerDto), staje się nieco trudny.

Należy zauważyć, że użycie przestarzałej i nieaktualnej metody statycznej Mapper.Map NIE jest opcją.

Odpowiedz

26

Od AutoMapper 5.1.1

Można dostać się do odwzorowującego za pomocą innego przeciążenie ResolveUsing z czterech parametrów, z których czwarty ResolutionContext (context.Mapper):

var config = new MapperConfiguration(
    cfg => { 
     cfg.CreateMap<Customer, CustomerDto>(); 
     cfg.CreateMap<Customer, DetailedCustomerDto>(); 
     cfg.CreateMap<Order, OrderDto>() 
      .ForMember(dst => dst.Customer, src => src.ResolveUsing((order, orderDto, i, context) => { 
       return order.Type == 1 
       ? context.Mapper.Map<Customer, CustomerDto>(order.Customer) 
       : context.Mapper.Map<Customer, DetailedCustomerDto>(order.Customer); 
     })); 
}); 

var orderTypeOne = new Order(); 
orderTypeOne.Type = 1; 
orderTypeOne.Customer = new Customer() { 
    Id = 1 
}; 

var dto = config.CreateMapper().Map<Order, OrderDto>(orderTypeOne); 
Debug.Assert(dto.Customer.GetType() == typeof (CustomerDto)); 

var orderTypeTwo = new Order(); 
orderTypeTwo.Type = 2; 
orderTypeTwo.Customer = new Customer() { 
    Id = 1 
}; 
dto = config.CreateMapper().Map<Order, OrderDto>(orderTypeTwo); 
Debug.Assert(dto.Customer.GetType() == typeof (DetailedCustomerDto)); 

Przed przystąpieniem do AutoMapper 5.1.1

Możesz dostać się do narzędzia odwzorowującego za pomocą innego przeciążenia: ResolveUsing z dwoma parametrami, z których pierwszy jest ResolutionResult (result.Context.Engine.Mapper):

var config = new MapperConfiguration(
    cfg => { 
     cfg.CreateMap<Customer, CustomerDto>(); 
     cfg.CreateMap<Customer, DetailedCustomerDto>(); 
     cfg.CreateMap<Order, OrderDto>() 
      .ForMember(dst => dst.Customer, src => src.ResolveUsing((result, order) => { 
       return order.Type == 1 
       ? result.Context.Engine.Mapper.Map<Customer, CustomerDto>(order.Customer) 
       : result.Context.Engine.Mapper.Map<Customer, DetailedCustomerDto>(order.Customer); 
     })); 
}); 

var orderTypeOne = new Order(); 
orderTypeOne.Type = 1; 
orderTypeOne.Customer = new Customer() { 
    Id = 1 
}; 

var dto = config.CreateMapper().Map<Order, OrderDto>(orderTypeOne); 
Debug.Assert(dto.Customer.GetType() == typeof (CustomerDto)); 

var orderTypeTwo = new Order(); 
orderTypeTwo.Type = 2; 
orderTypeTwo.Customer = new Customer() { 
    Id = 1 
}; 
dto = config.CreateMapper().Map<Order, OrderDto>(orderTypeTwo); 
Debug.Assert(dto.Customer.GetType() == typeof (DetailedCustomerDto)); 
+0

Świetnie, dziękuję. Właśnie tego szukałem. Szkoda, że ​​nie można go znaleźć w oficjalnej dokumentacji (a przynajmniej nie byłem w stanie tego zrobić). – Anton

+1

Po prostu dla FYI, jak z AutoMapper v5.1.1, parametr ResolutionContext zawierający instancję obiektu Mappera znajduje się teraz na 4. metodzie metody ResolveUsing (...), a właściwość "Engine" zniknęła. Więc byłoby ResolveUsing ((src, dest, wynik, kontekst) => { powrót context.Mapper.Map <.....>() } – nano2nd

+0

@ nano2nd, thx za wskazanie tego, że zostały zaktualizowane odpowiedź. – Anton

4

Oprócz wielkiego odpowiedź EVK, który pomógł mi, jeśli trzeba zrobić mapowanie wewnątrz odwzorowania wewnątrz config/profilu, który wymaga niestandardowy konstruktor (np typ ma domyślnego konstruktora), następujące będzie działać w v5.2.0:

CreateMap<Models.Job, Models.API.Job>(MemberList.Source); 

CreateMap<StaticPagedList<Models.Job>, StaticPagedList<Models.API.Job>>() 
       .ConstructUsing((source, context) => new StaticPagedList<Models.API.Job>(
        context.Mapper.Map<List<Models.Job>, List<Models.API.Job>>(source.ToList()), 
        source.PageNumber, 
        source.PageSize, 
        source.TotalItemCount)); 

W tym przykładzie mam mapowanie X.PagedList typ niestandardowy zestaw jednego typu obiektu na równoważną kolekcji innego typu obiektu . Pierwszym parametrem wyrażenia lamdba jest twój obiekt źródłowy, drugim jest twój ResolutionContext, z którego można uzyskać dostęp do instancji mappera, z której można mapować.

+0

Oh mój Boże, dziękuję! – Mason

Powiązane problemy