【问题标题】:How to use mapper.Map inside MapperConfiguration of AutoMapper?如何在 AutoMapper 的 MapperConfiguration 中使用 mapper.Map?
【发布时间】:2016-09-01 12:31:06
【问题描述】:

我需要使用 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)
            })
    );

客户端部分是:

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

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

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

从上面的代码可以看出,根据Order.Type 的值,映射器应该将属性Order.Customer 映射到不同的目标。由于一个目标 (DetailedCustomerDto) 从另一个目标 (CustomerDto) 继承,这就有点棘手了。

请注意,不能使用已过时且已弃用的静态方法 Mapper.Map

【问题讨论】:

    标签: c# .net mapping automapper


    【解决方案1】:

    自 AutoMapper 8.0 及更高版本起 以下 5.1.1 的答案仍然适用,但请注意 ResolveUsing 的使用已被 MapFrom 的重载替换,但签名在其他方面保持一致。

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

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

    【讨论】:

    • 太好了,谢谢。这正是我一直在寻找的。可惜在官方文档中找不到(或者至少我没能找到)。
    • 仅供参考,从 AutoMapper v5.1.1 开始,包含 Mapper 对象实例的 ResolutionContext 现在位于 ResolveUsing(...) 方法的第 4 个参数上,并且“Engine”属性为走了。所以它将是 ResolveUsing((src, dest, result, context) => { return context.Mapper.Map<.....>() }
    • @nano2nd,感谢您指出这一点,我已经更新了答案。
    【解决方案2】:

    除了帮助我的 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));
    

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

    【讨论】:

    • 救了我,非常感谢,正是我要找的。​​span>
    【解决方案3】:

    我正在使用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)
                }
            }
        }
    }
    

    我希望能帮助别人。

    【讨论】:

    • 确实在路上有点帮助。唯一的问题是您作为上下文获得的映射器实例不是IMapper 类型。它在您的示例中有效,但我需要它与 DI 注入的类型相同,即IMapper。有没有办法做到这一点?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-25
    • 1970-01-01
    • 1970-01-01
    • 2018-10-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多