You need to enable JavaScript to run this app. 使用Microsoft.Extensions.DependencyInjection时第二个作用域ScopedOperation构造未触发问题
优惠活动
大模型
产品
解决方案
定价
更多

使用Microsoft.Extensions.DependencyInjection时第二个作用域ScopedOperation构造未触发问题

核心问题根源:解析服务时使用了错误的容器实例

两个异常现象的本质都来自同一段代码的设计偏差:你在每个作用域块内,始终调用根服务提供器serviceProvider的GetService方法获取Scoped、Transient服务,完全没有使用当前作用域自带的scope.ServiceProvider完成服务解析。


为什么第二个作用域内ScopedOperation的构造函数没有触发

Scoped服务的生命周期绑定逻辑是:实例的存活范围完全由「解析该实例的容器」决定。
当你从根服务提供器(也就是BuildServiceProvider()返回的全局根容器)解析一个Scoped服务时,DI容器不会帮你关联后续手动创建的任何作用域,而是直接把这个Scoped实例缓存到根容器的实例列表中,以等价于单例的形式长期驻留。
你第二次在新作用域内获取ScopedOperation时,依然是从根容器取的缓存实例,自然不会触发构造函数——本质上这个Scoped实例从第一次被解析后就已经变成了全局单例,和你创建的两个作用域没有任何绑定关系。

为什么作用域释放时Scoped、Transient的Dispose方法没有触发

DI容器的实例销毁追踪逻辑是:容器只会负责销毁自己解析出来的实例。
你在作用域块内解析的所有Transient、Scoped服务,都是来自根容器的,这些实例的销毁责任归属根容器,和当前手动创建的scope没有关系。当你调用scope.Dispose()时,作用域容器只会遍历自己缓存的、由自身解析生成的实例列表完成销毁。因为你从未通过作用域容器解析过任何服务,它的追踪列表是空的,自然找不到对应的Scoped、Transient实例去触发Dispose逻辑。


修正方式

只需要把作用域块内的服务获取逻辑,从根容器改为使用当前作用域自带的服务提供器即可:

using (var scope = serviceProvider.CreateScope())
{
    // 这里使用 scope.ServiceProvider 而非外部的根 serviceProvider
    var singletonService = scope.ServiceProvider.GetService<SingletonOperation>();  
    var scopedService = scope.ServiceProvider.GetService<ScopedOperation>();  
    var transientService = scope.ServiceProvider.GetService<TransientOperation>();  
}

修正后符合预期的运行逻辑:

  • 每个作用域内首次解析Scoped服务时,都会重新触发构造函数生成新实例,实例的存活范围完全跟随当前作用域
  • 每个作用域解析生成的Scoped、Transient实例,都会被作用域容器追踪,在作用域释放时自动调用对应的Dispose方法
  • Singleton实例始终只创建一次,由根容器负责销毁

补充:微软官方DI容器默认不会在你从根容器解析Scoped服务时直接报错,这种悄悄把Scoped实例升级为根单例的行为是开发中非常高频的生命周期误区。如果需要拦截这类错误,可以在构建根服务提供器时传入validateScopes: true参数,容器会直接对这类越界解析抛出明确的异常,避免出现隐性的生命周期混乱。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.10 16:45:03