项目
版本

AutoMapper 映射继承

映射继承有两个作用:

  • 从基类或接口配置继承映射配置
  • 运行时多态映射

继承基类配置是可选的,您可以显式指定从基类型配置继承映射,使用 Include 或在派生类型配置中使用 IncludeBase

CreateMap<BaseEntity, BaseDto>()
   .Include<DerivedEntity, DerivedDto>()
   .ForMember(dest => dest.SomeMember, opt => opt.MapFrom(src => src.OtherMember));

CreateMap<DerivedEntity, DerivedDto>();

CreateMap<BaseEntity, BaseDto>()
   .ForMember(dest => dest.SomeMember, opt => opt.MapFrom(src => src.OtherMember));

CreateMap<DerivedEntity, DerivedDto>()
    .IncludeBase<BaseEntity, BaseDto>();

在上述每种情况下,派生映射都会继承自基映射的自定义配置。

Include/IncludeBase 递归应用,因此您只需包含层次结构中最接近的级别。

如果对于某个基类有许多直接派生类,为了方便起见,您可以从基类型映射配置中包含所有派生映射:

CreateMap<BaseEntity, BaseDto>()
    .IncludeAllDerived();

CreateMap<DerivedEntity, DerivedDto>();

请注意,这将搜索您的所有映射以查找派生类型,并且它会比显式指定派生映射慢。

运行时多态

考虑如下情况:

public class Order { }
public class OnlineOrder : Order { }
public class MailOrder : Order { }

public class OrderDto { }
public class OnlineOrderDto : OrderDto { }
public class MailOrderDto : OrderDto { }

var configuration = new MapperConfiguration(cfg => {
    cfg.CreateMap<Order, OrderDto>()
        .Include<OnlineOrder, OnlineOrderDto>()
        .Include<MailOrder, MailOrderDto>();
    cfg.CreateMap<OnlineOrder, OnlineOrderDto>();
    cfg.CreateMap<MailOrder, MailOrderDto>();
});

// 执行映射
var order = new OnlineOrder();
var mapped = mapper.Map(order, order.GetType(), typeof(OrderDto));
Assert.IsType<OnlineOrderDto>(mapped);

您会注意到,因为映射的对象是 OnlineOrder,AutoMapper 发现您有比 OrderDto 更具体的 OnlineOrderDto 映射,并自动选择了那个映射。

在派生类中指定继承

除了从基类配置继承外,您还可以在派生类中指定继承:

var configuration = new MapperConfiguration(cfg => {
  cfg.CreateMap<Order, OrderDto>()
    .ForMember(o => o.Id, m => m.MapFrom(s => s.OrderId));
  cfg.CreateMap<OnlineOrder, OnlineOrderDto>()
    .IncludeBase<Order, OrderDto>();
  cfg.CreateMap<MailOrder, MailOrderDto>()
    .IncludeBase<Order, OrderDto>();
});

As

对于简单的情况,您可以使用 As 将基映射重定向到现有的派生映射:

    cfg.CreateMap<Order, OnlineOrderDto>();
    cfg.CreateMap<Order, OrderDto>().As<OnlineOrderDto>();

    mapper.Map<OrderDto>(new Order()).ShouldBeOfType<OnlineOrderDto>();

继承映射优先级

这引入了额外的复杂性,因为有多种方式可以映射属性。这些来源的优先级如下:

  • 显式映射(使用 .MapFrom()
  • 继承的显式映射
  • 忽略属性映射
  • 约定映射(通过约定匹配的属性)

为了演示这一点,让我们修改上面展示的类

//领域对象
public class Order { }
public class OnlineOrder : Order
{
    public string Referrer { get; set; }
}
public class MailOrder : Order { }

//Dto
public class OrderDto
{
    public string Referrer { get; set; }
}

//映射
var configuration = new MapperConfiguration(cfg => {
    cfg.CreateMap<Order, OrderDto>()
        .Include<OnlineOrder, OrderDto>()
        .Include<MailOrder, OrderDto>()
        .ForMember(o=>o.Referrer, m=>m.Ignore());
    cfg.CreateMap<OnlineOrder, OrderDto>();
    cfg.CreateMap<MailOrder, OrderDto>();
});

// 执行映射
var order = new OnlineOrder { Referrer = "google" };
var mapped = mapper.Map(order, order.GetType(), typeof(OrderDto));
Assert.IsNull(mapped.Referrer);

请注意,在我们的映射配置中,我们忽略了 Referrer(因为它不存在于订单基类中),并且它的优先级高于约定映射,所以该属性没有被映射。

如果您确实希望在从 OnlineOrderOrderDto 的映射中映射 Referrer 属性,您应该像这样在映射中包含一个显式映射:

    cfg.CreateMap<OnlineOrder, OrderDto>()
        .ForMember(o=>o.Referrer, m=>m.MapFrom(x=>x.Referrer));

总的来说,此功能应使在使用继承的类上使用 AutoMapper 感觉更加自然。

在本文档中