AutoMapper嵌套字段被意外置空问题求助
看起来你遇到了映射库(比如AutoMapper)反向映射时意外覆盖未修改字段的问题——这种情况大多是因为默认的映射策略会用DTO的属性值完全覆盖实体,哪怕DTO里某些字段没被赋值(比如你的NotEditable字段,在DTO更新后可能是默认值null)。我来帮你分析原因并给出解决方案:
问题根源
当你执行Mapper.Map<Entity>(dto)把DTO转回实体时,默认情况下映射库会创建一个新的Entity实例,并把DTO里所有对应属性的值(包括嵌套对象的属性)赋值给新实例。如果你的DTO的Additional对象里没有设置NotEditable(或者DTO的Additional类压根没这个属性),那这个字段会被设为默认值null,替换掉原实体的原有值。
哪怕你想把更新应用到原实体,如果映射配置没做特殊处理,嵌套对象的未修改字段也会被覆盖。
解决方案
1. 直接更新原实体实例(推荐)
不要创建新的Entity,而是让映射库把DTO的变更合并到已有的原实体上。使用Map的重载方法,指定源(DTO)和目标(原实体):
// 原实体初始化 Entity e = new Entity("Id") { Additional = new EntityAdditional() { Editable = "Change Me!", NotEditable = "Don't Change Me!" } }; // 转DTO并更新 Dto dto = Mapper.Map<Dto>(e); dto.Additional.Editable = "Changed!"; // 关键:把DTO的变更映射回原实体,而非创建新实体 Mapper.Map(dto, e);
但这还不够,嵌套对象的NotEditable可能还是会被覆盖,需要配合映射配置调整。
2. 配置映射规则,保留未修改字段
针对嵌套类型的反向映射(DTOAdditional → EntityAdditional),配置映射规则,让映射库保留目标实体的原有字段值,不被DTO的默认值覆盖:
假设你用的是AutoMapper,配置代码如下:
var config = new MapperConfiguration(cfg => { // 正向映射:Entity → Dto cfg.CreateMap<Entity, Dto>(); cfg.CreateMap<EntityAdditional, DtoAdditional>(); // 反向映射:Dto → Entity cfg.CreateMap<Dto, Entity>(); // 核心配置:保留EntityAdditional的NotEditable原有值 cfg.CreateMap<DtoAdditional, EntityAdditional>() .ForMember(dest => dest.NotEditable, opt => opt.UseDestinationValue()); }); var mapper = config.CreateMapper();
UseDestinationValue()会告诉AutoMapper:当映射时,目标实体的NotEditable字段直接使用它原来的值,不要用DTO里的对应值(哪怕DTO里是null)。
如果你的DTO里根本没有NotEditable字段,也可以用Ignore()完全跳过这个字段的映射:
cfg.CreateMap<DtoAdditional, EntityAdditional>() .ForMember(dest => dest.NotEditable, opt => opt.Ignore());
3. 避免嵌套对象被重新实例化
如果你的DTO的Additional是一个全新的实例(而非从原实体映射来的对象),映射时可能会覆盖原实体的Additional对象。确保DTO的嵌套对象是从原实体映射而来,或者在配置中添加条件,只有当DTO的Additional不为null时才映射:
cfg.CreateMap<Dto, Entity>() .ForMember(dest => dest.Additional, opt => opt.Condition(src => src.Additional != null));
验证效果
做完以上配置后,执行你的流程,原实体的NotEditable字段应该会保留原来的"Don't Change Me!"值,而不会被设为null。
内容的提问来源于stack exchange,提问作者TomSelleck

