如何在C#中使用AutoMapper映射不同大小集合并保留目标元素
如何用AutoMapper实现源集合更新目标集合且保留目标集合原有元素?
问题场景
已知源集合(List<A>)包含2个元素,目标集合(List<B>)包含5个元素,调用mapper.Map(la, lb)后,目标集合被替换为仅包含2个元素,但期望实现按ID匹配更新对应元素,同时保留目标中未匹配的元素。
实体类定义
class A { public int Id; public string Name; } class B { public int Id; public string Name; public DateTime? DateOfBirth; }
示例数据
var la = new List<A> { new A { Id = 1, Name = "A1" }, new A { Id = 2, Name = "A2" } }; var lb = new List<B> { new B { Id = 1, Name = "B1", DateOfBirth = new DateTime(2011, 2, 12) }, new B { Id = 2, Name = "B2", DateOfBirth = new DateTime(2011, 3, 12) }, new B { Id = 3, Name = "B3", DateOfBirth = new DateTime(2011, 4, 12) }, new B { Id = 4, Name = "B4" }, new B { Id = 5, Name = "B5" } };
期望结果
映射后目标集合保留全部5个元素,仅更新ID匹配的元素:
{ { 1, "A1", "2011-2-12" }, { 2, "A2", "2011-3-12" }, { 3, "B3", "2011-4-12" }, { 4, "B4" }, { 5, "B5" } };
实际问题
调用mapper.Map(la, lb)后,目标集合仅保留前2个元素,丢失了ID为3、4、5的元素。
解决方案
AutoMapper默认的集合映射逻辑是直接替换整个目标集合,而非匹配更新元素。要实现需求,需指定集合映射的匹配规则,核心是用Id作为唯一匹配键。
方法1:通过自定义Profile配置映射规则
创建自定义Profile,配置单个对象映射规则,并自定义集合映射的转换逻辑,实现匹配更新:
public class MyProfile : Profile { public MyProfile() { // 配置A到B的单个对象映射,保留目标的DateOfBirth字段 CreateMap<A, B>() .ForMember(dest => dest.DateOfBirth, opt => opt.UseDestinationValue()); // 配置集合映射:遍历源集合,匹配目标中同ID元素并更新,保留未匹配元素 CreateMap<List<A>, List<B>>() .ConvertUsing((source, destination, context) => { foreach (var sourceItem in source) { var targetItem = destination.FirstOrDefault(b => b.Id == sourceItem.Id); if (targetItem != null) { context.Mapper.Map(sourceItem, targetItem); } } return destination; }); } }
方法2:手动遍历匹配更新
如果不想自定义Profile,也可以直接手动遍历源集合,匹配目标元素进行映射:
foreach (var a in la) { var b = lb.FirstOrDefault(x => x.Id == a.Id); if (b != null) { mapper.Map(a, b); } }
注意事项
- 确保
Id是唯一标识,用于准确匹配源和目标元素 - 若需要新增源中有但目标中没有的元素,可在遍历逻辑中添加判断,未找到匹配项时向目标集合添加新元素
- 单个对象映射时,若要保留目标的特定字段,需通过
UseDestinationValue()或Ignore()配置
内容的提问来源于stack exchange,提问作者Robert Watzen
相关产品推荐
相关产品推荐

