如何在AutoMapper的MapperConfiguration中使用mapper.Map?

Ant*_*ton 20 .net c# mapping automapper

我需要使用AutoMapper将对象映射到另一个对象.棘手的问题是如何在映射配置内或自定义类型转换器内访问映射器实例(IMapper实例)?

下面的代码不起作用,但它是我想要实现的一个例子 - 请注意mapper.Map调用并假设映射Customer => CustomerDtoCustomer => 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)
            })
    );
Run Code Online (Sandbox Code Playgroud)

客户端部分是:

var mapper = config.CreateMapper();
var orderDto = mapper.Map<Order, OrderDto>(order);
Run Code Online (Sandbox Code Playgroud)

我要映射的对象的简化版本是:

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; }
}
Run Code Online (Sandbox Code Playgroud)

从上面的代码中可以看出,基于值Order.Type,mapper应该将属性映射Order.Customer到不同的目标.当一个target(DetailedCustomerDto)继承自另一个(CustomerDto)时,它变得有点棘手.

请注意,过时和不推荐使用的静态方法Mapper.Map的使用不是一个选项.

Evk*_*Evk 45

截至AutoMapper 5.1.1

您可以使用ResolveUsing带有四个参数的另一个重载来访问映射器,其中第四个是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));
Run Code Online (Sandbox Code Playgroud)

在AutoMapper 5.1.1之前

您可以使用ResolveUsing带有两个参数的另一个重载来访问映射器,首先是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));
Run Code Online (Sandbox Code Playgroud)

  • 仅仅是FYI,从AutoMapper v5.1.1开始,包含Mapper对象实例的ResolutionContext现在位于ResolveUsing(...)方法的第4个参数上,并且"Engine"属性消失了.所以它将是ResolveUsing((src,dest,result,context)=> {return context.Mapper.Map <.....>()} (2认同)

Rod*_*una 8

我正在使用Automapper 9,上面的答案对我不起作用。然后为了解决我的问题,就像我使用的.afterMap一样,就像这样:

public class AutoMapperOrder : Profile
{
        public AutoMapperOrder()
        {
            CreateMap<Customer, CustomerDto>()
            //...

            CreateMap<Customer, DetailedCustomerDto>()
            //...

            CreateMap<Order, OrderDto>()
                .AfterMap((src, dest, context) => {
                dest.Customer = src.Type == 1
                    ? context.Mapper.Map<Customer, CustomerDto>(src.Customer)
                    : context.Mapper.Map<Customer, DetailedCustomerDto>(src.Customer)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望能帮助某人。


Bre*_*eno 7

除了Evk的好答案,这对我有帮助,如果你需要在需要自定义构造函数的配置/配置文件中的映射内部进行映射(即类型没有默认构造函数),以下内容将在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));
Run Code Online (Sandbox Code Playgroud)

在这个例子中,我将一个对象类型的X.PagedList自定义集合类型映射到另一个对象类型的等效集合.lamdba表达式的第一个参数是源对象,第二个参数是您ResolutionContext可以从中访问要映射的映射器实例.