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

为何HostBuilder可解析作用域服务,WebApplicationBuilder却报错?

问题解答

一、为什么HostBuilder能实例化?

在.NET 6之前的HostBuilder实现中,根服务提供者默认允许直接解析作用域服务(这其实是不符合DI设计原则的不规范实现)——作用域服务本该在专属的作用域内创建,直接从根容器获取会把它的生命周期强行提升为单例,可能引发资源泄漏或并发问题。而WebApplicationBuilder(.NET 6+)修复了这个漏洞,严格遵循DI规则:根服务提供者无法解析作用域服务,以此避免生命周期混乱,所以会抛出异常。

二、WebApplicationBuilder下的正确实现方式

正确的做法是让后台服务通过IServiceScopeFactory或IServiceProvider创建作用域,再在作用域内解析Transporter,绝对不能直接从根容器获取作用域服务。推荐用IServiceScopeFactory,它是专门用于创建作用域的服务,更符合DI最佳实践:

方式1:注入IServiceScopeFactory(推荐)

修改MyBackgroundService的构造函数,注入IServiceScopeFactory和单例的Config,然后在需要使用Transporter时创建作用域:

public class MyBackgroundService : BackgroundService
{
    private readonly Config _config;
    private readonly IServiceScopeFactory _scopeFactory;

    public MyBackgroundService(Config config, IServiceScopeFactory scopeFactory)
    {
        _config = config;
        _scopeFactory = scopeFactory;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            // 创建作用域,作用域内的服务会在using块结束后自动释放
            using var scope = _scopeFactory.CreateScope();
            var transporter = scope.ServiceProvider.GetRequiredService<Transporter>();
            
            // 执行业务逻辑
            await transporter.DoWorkAsync(stoppingToken);
            
            await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
        }
    }
}

服务注册时按常规方式添加即可:

builder.Services.AddSingleton<Config>();
builder.Services.AddScoped<Transporter>();
builder.Services.AddHostedService<MyBackgroundService>();

方式2:注入IServiceProvider(可行但不推荐)

如果一定要用IServiceProvider,也可以通过它创建作用域,但不如IServiceScopeFactory语义明确:

public class MyBackgroundService : BackgroundService
{
    private readonly Config _config;
    private readonly IServiceProvider _serviceProvider;

    public MyBackgroundService(Config config, IServiceProvider serviceProvider)
    {
        _config = config;
        _serviceProvider = serviceProvider;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            using var scope = _serviceProvider.CreateScope();
            var transporter = scope.ServiceProvider.GetRequiredService<Transporter>();
            
            // 执行业务逻辑
            await transporter.DoWorkAsync(stoppingToken);
            
            await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
        }
    }
}

关键注意点

  • 禁止从根服务提供者直接解析作用域服务,这会导致服务实例被长期持有,无法正确释放,还可能引发并发问题。
  • 后台服务是单例生命周期,所以注入的服务要么是单例,要么是能创建作用域的服务(如IServiceScopeFactory)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 12:25:01