AutoMapper接口列表映射配置断言失败问题排查与解决
问题背景
需要将SummaryDto映射到Summary,相关类和接口定义如下:
DTO层代码
public class SummaryDto { public List<IOrderDto> Orders { get; init; } } public interface IOrderDto { Guid Id { get; init; } } public class OnlineOrderDto : IOrderDto { public Guid Id { get; init; } public string WebsiteReference { get; init; } } public class ShopOrderDto : IOrderDto { public Guid Id { get; init; } public string ShopReference { get; init; } }
实体层代码
public class Summary { public List<IOrder> Orders { get; set; } } public interface IOrder { Guid Id { get; set; } } public class OnlineOrder : IOrder { public Guid Id { get; set; } public string WebsiteReference { get; set; } } public class ShopOrder : IOrder { public Guid Id { get; set; } public string ShopReference { get; set; } }
当前AutoMapper Profile配置
public class OrdersProfile : Profile { public OrdersProfile() { CreateMap<SummaryDto, Summary>(); CreateMap<ShopOrderDto, ShopOrder>(); CreateMap<ShopOrderDto, IOrder>() .As<ShopOrder>(); CreateMap<OnlineOrderDto, OnlineOrder>(); CreateMap<OnlineOrderDto, IOrder>() .As<OnlineOrder>(); } }
运行时正常工作的代码
var config = new MapperConfiguration(configure => configure.AddProfile<OrdersProfile>()); var mapper = config.CreateMapper(); var dto = new SummaryDto { Orders = new() { new ShopOrderDto { Id = Guid.NewGuid(), ShopReference = "Random shop" }, new OnlineOrderDto { Id = Guid.NewGuid(), WebsiteReference = "Random website" } } }; var summary = mapper.Map<Summary>(dto); // 运行正常,summary被正确填充
单元测试中的报错
单元测试代码:
public class MappingTests { [Fact] public void Configuration_IsValid() { // ARRANGE var config = new MapperConfiguration(configure => configure.AddProfile<OrdersProfile>()); // ASSERT config.AssertConfigurationIsValid(); // 抛出异常 // 异常信息: // AutoMapper.AutoMapperConfigurationException : The following member on ConsoleAppAutoMapper.Objects.Summary cannot be mapped: // Orders // Add a custom mapping expression, ignore, add a custom resolver, or modify the destination type ConsoleAppAutoMapper.Objects.Summary. // Context: // Mapping to member Orders from ConsoleAppAutoMapper.Dtos.SummaryDto to ConsoleAppAutoMapper.Objects.Summary // Exception of type 'AutoMapper.AutoMapperConfigurationException' was thrown. } }
问题核心:运行时能识别具体DTO类型并完成映射,但配置验证仅检查IOrderDto到IOrder的接口级映射,当前缺少该配置;直接添加CreateMap<IOrderDto, IOrder>()会破坏原有运行时映射逻辑。
解决方案
可以通过两种方式修改Profile,既满足配置验证要求,又不影响运行时映射:
方式一:使用ConstructUsing处理类型转换
为接口间映射添加自定义构造逻辑,根据具体DTO类型返回对应实体实例:
public class OrdersProfile : Profile { public OrdersProfile() { CreateMap<SummaryDto, Summary>(); // 具体DTO到具体实体的映射 CreateMap<ShopOrderDto, ShopOrder>(); CreateMap<OnlineOrderDto, OnlineOrder>(); // 接口到接口的映射,通过类型判断处理转换 CreateMap<IOrderDto, IOrder>() .ConstructUsing((dto, context) => dto switch { ShopOrderDto shopDto => context.Mapper.Map<ShopOrder>(shopDto), OnlineOrderDto onlineDto => context.Mapper.Map<OnlineOrder>(onlineDto), _ => throw new NotSupportedException($"不支持的IOrderDto类型:{dto.GetType()}") }); // 保留具体DTO到接口的映射(适配直接映射具体DTO到IOrder的场景) CreateMap<ShopOrderDto, IOrder>().As<ShopOrder>(); CreateMap<OnlineOrderDto, IOrder>().As<OnlineOrder>(); } }
方式二:使用Include关联具体类型映射
通过Include方法将具体类型的映射关联到接口映射中,让AutoMapper自动识别实现类:
public class OrdersProfile : Profile { public OrdersProfile() { CreateMap<SummaryDto, Summary>(); // 定义接口到接口的映射,并关联具体类型的映射规则 CreateMap<IOrderDto, IOrder>() .Include<ShopOrderDto, ShopOrder>() .Include<OnlineOrderDto, OnlineOrder>(); // 具体DTO到具体实体的映射 CreateMap<ShopOrderDto, ShopOrder>(); CreateMap<OnlineOrderDto, OnlineOrder>(); // 具体DTO到接口的映射 CreateMap<ShopOrderDto, IOrder>().As<ShopOrder>(); CreateMap<OnlineOrderDto, IOrder>().As<OnlineOrder>(); } }
两种方案均可让config.AssertConfigurationIsValid()通过验证,同时保持运行时的正确映射逻辑。
内容的提问来源于stack exchange,提问作者Arthur Rey
相关产品推荐
相关产品推荐

