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

AutoMapper依赖注入缓存导致映射含共享类的不同类时出现500错误

AutoMapper + DI 下共享自定义集合类型导致的映射空引用异常分析与解决

问题场景

我们有一个使用AutoMapper和依赖注入的C# Web服务,控制器包含A、B两个接口,各自有独立的DTO与资源类,调用时都会执行DTO到资源类的映射。两个DTO均包含**AutoMappingSharedCollection**类型的属性。

IIS重置后,按「先调用A再调用B」的顺序操作时,B接口会触发嵌套的AutoMapper异常,最终根源是NullReferenceException。临时解决办法有两个:

  • 在控制器内本地创建Mapper实例(而非使用DI注入的实例),不会出错
  • 将B接口DTO中的自定义集合替换为普通List<T>,也能规避错误

原因分析

核心问题出在AutoMapper的映射缓存机制与DI单例生命周期的冲突:

  1. AutoMapper首次处理映射时,会自动生成并缓存类型映射计划,对于自定义集合类型,会生成对应的集合转换逻辑
  2. 首次调用A接口时,AutoMapper为AutoMappingSharedCollection<AutoMappingSharedClass>生成了到List<AutoMappingSharedClassMapped>的映射逻辑并缓存
  3. 由于DI注入的Mapper默认是单例,这个缓存会被所有后续请求共享
  4. 当调用B接口时,B的目标集合是AutoMappingSharedCollection<AutoMappingSharedClassMapped>,但AutoMapper错误复用了之前缓存的「到List」的映射逻辑,无法正确初始化B的自定义集合实例,最终抛出空引用异常

注:如果先调用B,AutoMapper会先缓存自定义集合到自定义集合的映射逻辑,后续调用A时也能正常适配,因此不会出错。

规避方案

方案1:显式配置自定义集合的映射规则

在AutoMapper Profile中,为自定义集合的不同目标类型显式编写映射逻辑,避免AutoMapper自动推断错误的缓存:

public AutoMappingWeb()
{
    // 保留原有实体类映射配置
    CreateMap<AutoMappingClassALarge, AutoMappingClassALargeMapped>()
        .ForMember(dest => dest.ClassAMappedList, opt => opt.MapFrom(src => src.ClassAList));
    CreateMap<AutoMappingClassA, AutoMappingClassAMapped>()
        .ForMember(dest => dest.Data, opt => opt.MapFrom(src => src.Data));
    CreateMap<AutoMappingSharedClass, AutoMappingSharedClassMapped>()
        .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name));
    CreateMap<AutoMappingClassBLarge, AutoMappingClassBLargeMapped>();
    CreateMap<AutoMappingClassB, AutoMappingClassBMapped>();

    // 显式配置自定义集合到List的映射
    CreateMap<AutoMappingSharedCollection<AutoMappingSharedClass>, List<AutoMappingSharedClassMapped>>()
        .ConvertUsing((src, _, context) => 
            src.Select(item => context.Mapper.Map<AutoMappingSharedClassMapped>(item)).ToList());

    // 显式配置自定义集合到自定义集合的映射
    CreateMap<AutoMappingSharedCollection<AutoMappingSharedClass>, AutoMappingSharedCollection<AutoMappingSharedClassMapped>>()
        .ConvertUsing((src, _, context) => 
        {
            var result = new AutoMappingSharedCollection<AutoMappingSharedClassMapped>();
            foreach (var item in src)
            {
                result.Add(context.Mapper.Map<AutoMappingSharedClassMapped>(item));
            }
            return result;
        });
}

方案2:调整Mapper的DI生命周期

将注入的Mapper从单例改为**范围(Scoped)**生命周期,让每个请求都使用独立的Mapper实例,避免跨请求的缓存冲突:
在Program.cs(或Startup.cs)中修改DI配置:

// 替换原有的AddAutoMapper注册方式
services.AddAutoMapper(typeof(AutoMappingWeb));
// 显式注册Scoped的IMapper
services.AddScoped<IMapper>(sp => 
{
    var config = sp.GetRequiredService<MapperConfiguration>();
    return config.CreateMapper();
});

方案3:统一目标集合类型

如果业务允许,将A、B接口的目标集合类型统一(比如都使用自定义集合或都使用List<T>),消除AutoMapper缓存混淆的前提。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 00:50:21