You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AutoMapper同实体集合映射空引用异常及密码映射问题排查

问题分析与解决方案:AutoMapper同实体映射的空引用与密码更新失效问题

问题描述

使用AutoMapper进行同类型(Conturi→Conturi)实体映射时遇到两个核心问题:

  1. 从数据库获取的实体中AdreseConturi集合为null,执行_mapper.Map(updatedUser, currentUser)时触发NullReferenceException。尝试过两种映射配置:忽略所有成员的简单映射、构造函数映射(构造函数会将null集合转为HashSet<Adrese>),均未解决报错。给AdreseConturi添加setter后空引用问题消失,但密码更新失效——构造函数内已获取到新密码值,但最终实体的Parola属性未更新。

问题根源

1. NullReferenceException 根源

  • 调用Map(source, destination)这种原地更新重载时,AutoMapper不会调用目标对象的构造函数,而是直接尝试为目标对象的属性赋值。
  • 原Conturi类的AdreseConturi只有getter无setter,且从数据库获取的currentUser.AdreseConturi为null。AutoMapper默认会尝试对ICollection类型属性执行集合合并操作(调用Add方法),但null集合无法调用Add方法,直接触发空引用异常。
  • 构造函数映射仅在创建新对象场景(如Map<Conturi>(source))生效,原地更新时不会执行构造逻辑。

2. 密码更新失效根源

  • 定义了两套同类型的AutoMapper映射规则,AutoMapper只会使用最后注册的规则,且原地更新场景下不会触发构造函数逻辑。
  • 即使构造函数映射生效,原地更新时AutoMapper不会替换整个目标对象,而是逐个属性赋值;若未明确配置Parola属性的映射规则,会默认保留原对象值。

解决方案

针对空引用异常

有两种可行方案:

方案1:添加私有setter并配置集合映射(推荐,保留封装性)

修改Conturi类的AdreseConturi属性:

public ICollection<Adrese>? AdreseConturi { get; private set; }

配置AutoMapper规则:

CreateMap<Conturi, Conturi>()
    .ForMember(dest => dest.AdreseConturi, opt => 
    {
        // 将null集合转为空HashSet
        opt.MapFrom(src => src.AdreseConturi ?? new HashSet<Adrese>());
        // 优化集合映射(合并而非替换,按需选择)
        opt.OptimizeCollections();
    });

方案2:原地更新前手动初始化null集合

在映射前手动处理null集合:

if(currentUser.AdreseConturi == null)
{
    currentUser.AdreseConturi = new HashSet<Adrese>();
}
_mapper.Map(updatedUser, currentUser);

针对密码更新失效

保留一套有效的AutoMapper配置,明确指定Parola属性的映射规则:

// 覆盖原有两套配置,只保留这一套
CreateMap<Conturi, Conturi>()
    // 忽略所有不需要更新的成员
    .ForAllMembers(opt => opt.Ignore())
    // 仅更新Parola属性,从上下文Items中获取新密码
    .ForMember(dest => dest.Parola, opt => 
        opt.MapFrom((src, dest, _, context) => 
            context.Items.TryGetValue("NewPassword", out var newPwd) ? (string)newPwd : dest.Parola))
    // 处理AdreseConturi集合,避免空引用
    .ForMember(dest => dest.AdreseConturi, opt => 
    {
        opt.MapFrom(src => src.AdreseConturi ?? new HashSet<Adrese>());
        opt.OptimizeCollections();
    });

修改调用代码,直接原地更新(无需先创建updatedUser):

var currentUser = await extendedRepository.GetByTokenAsync(changePasswordDto.TokenProp);
if (currentUser == null)
{
    throw new DbUpdateException("User not found");
}

// 若使用方案1,此初始化步骤可省略
if(currentUser.AdreseConturi == null)
{
    currentUser.AdreseConturi = new HashSet<Adrese>();
}

// 直接原地更新,传入新密码参数
_mapper.Map(currentUser, currentUser, 
    opt => opt.Items["NewPassword"] = changePasswordDto.NewHashedPasswordProp);

原报错信息

AutoMapper.AutoMapperMappingException: Error mapping types.
Mapping types:
Conturi -> Conturi
E_Commerce_BackEnd.Models.UserRelatedModels.Conturi -> E_Commerce_BackEnd.Models.UserRelatedModels.Conturi
Type Map configuration:
Conturi -> Conturi
E_Commerce_BackEnd.Models.UserRelatedModels.Conturi -> E_Commerce_BackEnd.Models.UserRelatedModels.Conturi
Destination Member:
AdreseConturi
---> System.NullReferenceException: Object reference not set to an instance of an object.

内容的提问来源于stack exchange,提问作者Misu Stefan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 18:53:22