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
(因为它不存在于订单基类中),并且它的优先级高于约定映射,所以该属性没有被映射。
如果您确实希望在从 OnlineOrder
到 OrderDto
的映射中映射 Referrer
属性,您应该像这样在映射中包含一个显式映射:
cfg.CreateMap<OnlineOrder, OrderDto>()
.ForMember(o=>o.Referrer, m=>m.MapFrom(x=>x.Referrer));
总的来说,此功能应使在使用继承的类上使用 AutoMapper 感觉更加自然。