DI上下文已释放后,如何在后台线程中使用AutoMapper?
问题分析
你遇到的问题根源在于:通过DI注入的IMapper实例可能绑定了请求作用域的上下文——要么是AutoMapper本身被注册为作用域服务,要么是你的映射配置(Profile)中依赖了作用域内的服务,导致AutoMapper在后台任务执行时仍尝试访问已释放的请求作用域。
解决方案
1. 手动注册单例模式的IMapper
绕过默认的AddAutoMapper扩展,直接注册一个不依赖请求作用域的单例IMapper实例,确保它在应用启动时就完成初始化,后续无需访问DI容器:
services.AddSingleton<IMapper>(sp => { var config = new MapperConfiguration(cfg => { // 添加上你的所有映射Profile cfg.AddProfile<YourMappingProfile>(); cfg.AddProfile<AnotherProfile>(); }); // 验证配置正确性(可选,但建议开启) config.AssertConfigurationIsValid(); return config.CreateMapper(); });
这种方式创建的IMapper完全独立于请求作用域,不会绑定任何DI上下文。
2. 清理映射配置中的作用域依赖
检查你的Profile实现,确保没有在映射逻辑中依赖作用域内的服务(比如通过ConstructUsing或ResolveUsing从DI获取服务)。如果必须使用外部依赖,改为在映射时通过参数传入:
// 错误示例(依赖DI作用域) cfg.CreateMap<Source, Dest>() .ConstructUsing(sp => new Dest(sp.GetRequiredService<IScopedService>())); // 正确示例(映射时传入依赖) cfg.CreateMap<Source, Dest>() .ForMember(d => d.Property, opt => opt.MapFrom((src, dest, _, context) => context.Items["RequiredService"].DoSomething(src))); // 后台任务中使用时传入依赖 var dest = mapper.Map<Source, Dest>(source, opt => opt.Items["RequiredService"] = preResolvedService);
3. 在后台任务中创建独立的Mapper实例
如果无法修改全局注册方式,可以在服务类中注入IMapperConfigurationProvider,然后在后台任务里基于配置创建新的IMapper实例——这个实例不会绑定原请求作用域:
public class YourService { private readonly IMapperConfigurationProvider _mapperConfig; public YourService(IMapperConfigurationProvider mapperConfig) { _mapperConfig = mapperConfig; } public Task<IActionResult> YourApiMethod() { // 启动后台任务 _ = Task.Run(() => { // 创建独立的Mapper实例 var mapper = _mapperConfig.CreateMapper(); // 使用mapper执行映射操作 var result = mapper.Map<Source, Dest>(yourSourceData); }); return Task.FromResult<IActionResult>(Ok()); } }
内容的提问来源于stack exchange,提问作者Bassinator
相关产品推荐
相关产品推荐

