为何单个依赖注入作用域内的Scoped服务会被实例化两次?
问题根因
你遇到的是.NET内置依赖注入框架中生命周期不匹配导致的作用域隔离问题,核心逻辑如下:
- Singleton服务的全局唯一实例是绑定到根服务提供者的,无论你在哪个子作用域第一次触发Singleton的实例化,它的依赖解析都会走根容器逻辑
- 根容器没有子作用域的上下文,解析Singleton依赖的Scoped服务时,会直接生成一个和根容器生命周期绑定的「根作用域Scoped实例」,这个实例会被Singleton永久持有
- 你后续在自定义子作用域中单独获取Scoped服务时,会走当前子作用域的缓存逻辑,生成属于当前作用域的全新Scoped实例,因此出现两次实例化输出
执行流程拆解
你的代码实际运行顺序完全符合框架设计:
- 调用
BuildServiceProvider()生成根服务提供者,此时Singleton未实例化 - 创建自定义子作用域,拿到当前作用域的服务提供者
scopeProvider - 调用
scopeProvider.GetRequiredService<Singleton>():- 框架检测到Singleton未实例化,先在根容器中创建Singleton实例
- 解析Singleton构造参数的
Scoped服务,根容器无当前子作用域上下文,生成第一个Scoped实例(输出scoped 1),注入给Singleton - Singleton实例化完成,输出
singleton 1
- 调用
scopeProvider.GetRequiredService<Scoped>():- 检测当前子作用域缓存中无Scoped实例,生成第二个属于当前作用域的Scoped实例,输出
scoped 2
- 检测当前子作用域缓存中无Scoped实例,生成第二个属于当前作用域的Scoped实例,输出
修复方案
如果要实现你预期的「同一个作用域内Scoped仅实例化一次」的效果,可选方案如下:
- 方案1:取消Scoped服务对Singleton构造函数的直接注入,改为在Singleton中注入
IServiceScopeFactory,需要使用Scoped服务时手动创建作用域解析,避免生命周期绑定 - 方案2:如果该Scoped服务全局仅需一个实例,直接将其注册为Singleton即可
- 方案3:开启作用域校验,构建服务提供者时传入参数
services.BuildServiceProvider(validateScopes: true),此时生命周期不匹配的代码会直接抛出异常,提前暴露逻辑漏洞,避免隐式生成多个实例
内容的提问来源于stack exchange,提问作者Tomas
相关产品推荐
相关产品推荐

