web-dev-qa-db-fra.com

Comment utiliser mapper.Map dans MapperConfiguration d'AutoMapper?

J'ai besoin de mapper un objet à un autre à l'aide d'AutoMapper. La question délicate est de savoir comment puis-je accéder à une instance du mappeur (instance d'IMapper) à l'intérieur de la configuration de mappage ou à l'intérieur d'un convertisseur de type personnalisé?

Le code ci-dessous ne fonctionne pas, mais c'est un exemple de ce que j'aimerais réaliser - veuillez noter le mapper.Map appelle et suppose que les mappages Customer => CustomerDto et Customer => DetailedCustomerDto sont définis.

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 partie client est:

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

La version simplifiée des objets que je souhaite cartographier est:

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

Comme vous le voyez dans les codes ci-dessus, sur la base de la valeur de Order.Type, le mappeur doit mapper la propriété Order.Customer vers différentes cibles. Comme une cible (DetailedCustomerDto) hérite de l'autre (CustomerDto), cela devient un peu délicat.

Veuillez noter que l'utilisation de la méthode statique obsolète et obsolète Mapper.Map n'est PAS une option.

20
Anton

à partir d'AutoMapper 5.1.1

Vous pouvez accéder au mappeur en utilisant une autre surcharge de ResolveUsing avec quatre paramètres, dont le quatrième est 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));

Avant AutoMapper 5.1.1

Vous pouvez accéder au mappeur en utilisant une autre surcharge de ResolveUsing avec deux paramètres, dont le premier est 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));
43
Evk

En plus de l'excellente réponse d'Evk, qui m'a aidé, si vous avez besoin de faire un mappage à l'intérieur d'un mappage à l'intérieur d'une configuration/profil qui nécessite un constructeur personnalisé (c'est-à-dire que le type n'a pas de constructeur par défaut), ce qui suit fonctionnera dans la 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));

Dans cet exemple, je mappe le type de collection personnalisé X.PagedList d'un type d'objet sur une collection équivalente d'un autre type d'objet. Le premier paramètre de l'expression lamdba est votre objet source, le second est votre ResolutionContext à partir duquel vous pouvez accéder à une instance de mappeur à partir de laquelle mapper.

7
Breeno