ASP.NET Core多单例获取含Scoped依赖的Transient服务异常问题
问题场景
在ASP.NET Core中注册两个单例服务SingletonA和SingletonB,两者都通过服务提供者调用GetRequiredService<TransientServiceA>()获取依赖。TransientServiceA的构造函数依赖Scoped服务ScopedServiceA。出现的异常情况是:仅注册其中一个单例时,依赖注入能正常工作;同时注册两个单例时,抛出无法解析TransientServiceA的错误,提示该服务依赖Scoped服务,无法从根提供者解析。
代码示例
服务注册代码
services.AddScoped<ScopedServiceA>(); services.AddSingleton<SingletonA>(serviceprovider => return serviceprovider.GetRequiredService<TransientServiceA>();); services.AddSingleton<SingletonB>(serviceprovider => return serviceprovider.GetRequiredService<TransientServiceA>();); services.AddTransient<TransientServiceA>();
TransientServiceA构造函数
public TransientServiceA(ScopedServiceA scopedSrv) { ... }
错误信息
Cannot resolve 'TransientServiceA' from root provider because it requires scoped service
核心原因
ASP.NET Core的DI容器中,根服务提供者不允许解析依赖Scoped服务的对象——Scoped服务的生命周期绑定在特定作用域(比如请求作用域)内,根提供者没有对应的作用域上下文。
仅注册一个单例时能正常工作,是因为DI容器的延迟初始化特性:
- 若只注册
SingletonA,且应用启动阶段未主动解析该单例,它会直到第一次被使用时才初始化。如果第一次使用发生在请求作用域内(比如Controller构造函数注入SingletonA),此时会用请求作用域的服务提供者解析TransientServiceA,而请求作用域包含Scoped服务上下文,因此能正常解析。 - 同时注册两个单例时,DI容器会在启动阶段对单例做提前验证或初始化,此时使用根服务提供者,解析
TransientServiceA时发现其依赖Scoped服务,直接抛出异常。
解决方案
方案1:延迟获取Transient服务
不在单例初始化阶段直接获取TransientServiceA,而是注入IServiceProvider,在需要使用时确保在有效作用域内获取:
// 注册SingletonA services.AddSingleton<SingletonA>(serviceprovider => new SingletonA(serviceprovider)); // SingletonA实现 public class SingletonA { private readonly IServiceProvider _serviceProvider; public SingletonA(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public void Execute() { // 创建新作用域或使用当前请求作用域获取服务 using (var scope = _serviceProvider.CreateScope()) { var transientSrv = scope.ServiceProvider.GetRequiredService<TransientServiceA>(); // 执行业务逻辑 } } }
按同样方式修改SingletonB的注册与实现,确保每次获取TransientServiceA时都处于有效作用域内,能正确解析Scoped依赖。
方案2:调整服务生命周期(谨慎使用)
如果业务场景允许,可将ScopedServiceA改为Transient生命周期,这样TransientServiceA不再依赖Scoped服务,根提供者可直接解析。但该改动会改变服务的生命周期行为,可能引发业务逻辑问题,需结合实际场景评估。
内容的提问来源于stack exchange,提问作者Vorasoda

