2016-05-06 20 views
12

Ho bisogno di associare un oggetto a un altro utilizzando AutoMapper. La domanda complicata è: come posso accedere a un'istanza del programma di mappatura (istanza di IMapper) all'interno della configurazione di mappatura o all'interno di un convertitore di tipi personalizzati?Come utilizzare mapper.Map all'interno di MapperConfigurazione di AutoMapper?

Il codice riportato di seguito non funziona, tuttavia è un esempio di ciò che vorrei ottenere - si prega di notare le chiamate mapper.Map e si assume che i mapping Customer => CustomerDto e Customer => DetailedCustomerDto siano definiti.

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

La parte client è:

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

La versione semplificata di oggetti che voglio mappa è:

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

Come si può vedere dai codici di cui sopra, in base al valore di Order.Type, il mapper deve associare la proprietà Order.Customer a destinazioni diverse. Poiché un obiettivo (DetailedCustomerDto) eredita dall'altro (CustomerDto) diventa un po 'complicato.

Si prega di notare che l'utilizzo del metodo statico obsoleto e deprecato Mapper.Map NON è un'opzione.

risposta

26

A partire dal automapper 5.1.1

Si può raggiungere il mapper utilizzando un altro sovraccarico ResolveUsing con quattro parametri, quarto dei quali è 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)); 

Prima di AutoMapper 5.1.1

È possibile raggiungere il mappatore utilizzando un altro sovraccarico di ResolveUsing con due parametri, prima delle quali è 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

Ottimo, grazie. Questo e 'esattamente quello che stavo cercando. Che peccato non può essere trovato nella documentazione ufficiale (o almeno non ero in grado di farlo). – Anton

+1

Solo per FYI, a partire da AutoMapper v5.1.1 il ResolutionContext che contiene l'istanza dell'oggetto Mapper è ora sul 4o argomento del metodo ResolveUsing (...) e la proprietà "Engine" è scomparsa. Quindi sarebbe ResolveUsing ((src, dest, risultato, contesto) => { ritorno context.Mapper.Map <.....>()} – nano2nd

+0

@ nano2nd, thx per la precisazione, ho aggiornato la risposta. – Anton

4

Oltre alla grande risposta di EVK, che mi ha aiutato, se avete bisogno di fare una mappatura all'interno di una mappatura all'interno di una configurazione/profilo che richiede un costruttore personalizzato (es il tipo non ha un costruttore di default), il seguente sarà lavorare in 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)); 

In questo esempio sto mappatura del X.PagedList tipo raccolta personalizzata di un tipo di oggetto su una raccolta pari di un altro tipo di oggetto . Il primo parametro per l'espressione lamdba è il tuo oggetto sorgente, il secondo è il tuo ResolutionContext da cui puoi accedere a un'istanza del mapper da cui eseguire la mappatura.

+0

Oh mio dio, grazie! – Mason