如何使用Automapper实现带条件的对象列表映射(含ProductType示例)
使用AutoMapper实现带条件的ProductType列表映射
针对你的问题,我来一步步讲解如何用AutoMapper实现带条件的对象列表映射,覆盖单个对象的字段条件处理和列表内父-子关系关联两种常见场景:
1. 先定义API视图模型
首先创建和领域对象对应的视图模型,你可以根据API的实际需求调整字段(比如把int类型的Active转成更符合API习惯的bool):
public class ProductTypeViewModel { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public int ParentProductTypeId { get; set; } public bool IsActive { get; set; } public DateTime UpdatedOnUtc { get; set; } }
另外提一句:你的领域对象里字段名拼写错了——PerentProductTypeId应该是ParentProductTypeId,建议修正这个拼写,避免后续出现不必要的bug。
2. 配置AutoMapper映射规则
创建一个继承自Profile的配置类,在这里定义核心的映射逻辑,包括你需要的条件判断:
场景1:单个对象的条件字段映射
如果你的需求是针对单个ProductType,根据业务规则判断它是否是某个父类型的子集,再设置对应的ParentProductTypeId,可以用ForMember结合MapFrom或ResolveUsing实现:
public class ProductTypeProfile : Profile { public ProductTypeProfile() { CreateMap<ProductType, ProductTypeViewModel>() // 常规字段映射:把int类型的Active转成bool .ForMember(dest => dest.IsActive, opt => opt.MapFrom(src => src.Active == 1)) // 处理ParentProductTypeId的条件逻辑 .ForMember(dest => dest.ParentProductTypeId, opt => { // 示例:如果产品名称包含"T恤",则设置父ID为休闲服饰的ID(假设休闲服饰ID是1) opt.MapFrom(src => src.Name.Contains("T恤") ? 1 : src.PerentProductTypeId); // 如果你的条件逻辑需要依赖外部数据(比如从数据库查父类型),可以用ResolveUsing // opt.ResolveUsing((src, dest, _, context) => // { // // 这里可以通过上下文注入服务,或者传递额外数据 // var parentService = context.GetService<IParentProductTypeService>(); // return parentService.GetParentIdForSubset(src) ?? src.PerentProductTypeId; // }); }); } }
场景2:列表内的父-子关系映射
如果你的需求是在映射整个ProductType列表时,自动识别列表内的父-子关系,确保子类型的ParentProductTypeId正确对应父类型的ID,可以通过AutoMapper的上下文传递整个列表,再在映射逻辑中做判断:
public class ProductTypeProfile : Profile { public ProductTypeProfile() { CreateMap<ProductType, ProductTypeViewModel>() .ForMember(dest => dest.IsActive, opt => opt.MapFrom(src => src.Active == 1)) .ForMember(dest => dest.ParentProductTypeId, opt => { opt.ResolveUsing((src, dest, _, context) => { // 从上下文获取整个ProductType列表 var allProductTypes = context.Items["AllProductTypes"] as IEnumerable<ProductType>; if (allProductTypes == null) return src.PerentProductTypeId; // 自定义子集判断逻辑:判断当前产品是否是列表中某个父类型的子集 var parentType = allProductTypes.FirstOrDefault(pt => IsSubsetOf(src, pt)); return parentType?.Id ?? src.PerentProductTypeId; }); }); } // 封装子集判断的业务逻辑 private bool IsSubsetOf(ProductType child, ProductType parent) { // 示例规则:根据名称判断父子关系 if (parent.Name == "休闲服饰" && (child.Name == "T恤" || child.Name == "牛仔裤")) { return true; } if (parent.Name == "办公文具" && (child.Name == "笔记本" || child.Name == "钢笔")) { return true; } return false; } }
3. 执行映射(以ASP.NET Core为例)
注册AutoMapper
在Program.cs中注册AutoMapper服务:
builder.Services.AddAutoMapper(typeof(ProductTypeProfile));
在控制器中使用
通过依赖注入获取IMapper实例,执行映射时把整个列表传入上下文:
public class ProductTypeController : ControllerBase { private readonly IMapper _mapper; private readonly ApplicationDbContext _dbContext; public ProductTypeController(IMapper mapper, ApplicationDbContext dbContext) { _mapper = mapper; _dbContext = dbContext; } [HttpGet] public async Task<ActionResult<List<ProductTypeViewModel>>> GetAll() { var productTypes = await _dbContext.ProductTypes.ToListAsync(); // 传递整个列表到AutoMapper上下文,供映射逻辑使用 var viewModels = _mapper.Map<List<ProductTypeViewModel>>(productTypes, opt => { opt.Items["AllProductTypes"] = productTypes; }); return Ok(viewModels); } }
小提示
- 如果你的子集判断逻辑很复杂,建议把它封装到单独的业务服务类中,不要直接写在Profile里,保持映射配置的简洁性。
- 如果存在顶级分类(没有父类型),记得处理
ParentProductTypeId的空值/默认值情况,避免视图模型返回无效数据。
内容的提问来源于stack exchange,提问作者AshanMG
相关产品推荐
相关产品推荐

