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

为何C#中Singleton对象可依赖Transient却不能依赖Scoped对象?

为什么Singleton依赖Transient可行,但依赖Scoped会在Azure Functions中报错?

问题重现

类定义

public Dependent(Parent parent)
{
    Parent = parent;
}

正常运行的DI配置

当Parent设置为Transient生命周期时,代码可正常执行:

public override void Configure(IFunctionsHostBuilder builder)
{
    builder.Services.AddTransient<Parent>();
    builder.Services.AddSingleton<Dependent>();
}

报错场景及错误信息

将Parent改为Scoped生命周期后,触发以下错误:

public override void Configure(IFunctionsHostBuilder builder)
{
    builder.Services.AddScoped<Parent>();
    builder.Services.AddSingleton<Dependent>();
}

错误日志:

[2023-09-28T11:14:54.195Z] Executed 'Function1' (Failed, Id=19772818-9385-4382-8c53-116589abb2fa, Duration=1ms)
[2023-09-28T11:14:54.199Z] Microsoft.Azure.WebJobs.Script.WebHost: Dependency FunctionApp1.Parent {ReturnDefault} as parameter "parent" reuse CurrentScopeReuse {Lifespan=100} lifespan shorter than its parent's: singleton FunctionApp1.Dependent {ReturnDefault} #150
[2023-09-28T11:14:54.204Z] from scoped container with {no name}

核心原因解析

  • Transient实例的特性:Transient对象每次被请求时都会生成全新实例。当Singleton的Dependent初始化时,容器会创建一个专属的Parent实例绑定到它身上——这个Parent的生命周期会和Dependent完全同步,直到Singleton被销毁,不存在生命周期不匹配的风险,因此容器允许这种配置。

  • Scoped实例的特性:Scoped对象的生命周期严格绑定到当前作用域(在Azure Functions中,通常对应单个函数执行的生命周期)。当Singleton依赖Scoped对象时:

    1. Singleton在应用启动(或首次请求)时初始化,会从某个临时作用域中获取Parent实例。
    2. 当该作用域结束(比如函数执行完成),容器会自动回收这个Scoped的Parent实例。
    3. 后续Singleton再使用这个已被回收的Parent,会引发空引用、状态异常等问题。

Azure Functions的DI容器会主动检测这种短生命周期服务被长生命周期服务持有的风险,直接抛出错误阻止潜在的资源泄漏或逻辑异常。

解决思路

如果需要让Singleton使用Scoped服务,可采用以下两种方案:

  • 注入IServiceScopeFactory,在需要使用Parent时动态创建作用域并获取实例:
    public class Dependent
    {
        private readonly IServiceScopeFactory _scopeFactory;
    
        public Dependent(IServiceScopeFactory scopeFactory)
        {
            _scopeFactory = scopeFactory;
        }
    
        public void DoWork()
        {
            using var scope = _scopeFactory.CreateScope();
            var parent = scope.ServiceProvider.GetRequiredService<Parent>();
            // 执行业务操作
        }
    }
    
  • 将Dependent的生命周期改为Scoped,和Parent保持一致,确保每次函数执行都会创建配套的Dependent和Parent实例,符合Scoped的生命周期规则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 04:30:08