在单例服务中注入ITokenAcquisition在Development环境下失效
问题解答:后台服务中使用ITokenAcquisition的异常分析
1. 是否允许在后台服务中注入ITokenAcquisition?
不建议直接注入。ITokenAcquisition默认是Scoped生命周期的服务,而后台服务(IHostedService)属于Singleton生命周期。在.NET依赖注入系统中,单例服务不能直接依赖Scoped服务——这会导致Scoped服务被单例长期持有,失去其“作用域内唯一”的特性,可能引发线程安全问题或状态污染。
正确的做法是通过注入IServiceProvider,在需要使用ITokenAcquisition时临时创建作用域,获取Scoped实例:
public class QueuedHostedService : BackgroundService { private readonly IServiceProvider _serviceProvider; public QueuedHostedService(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } private async Task BackgroundProcessing(CancellationToken stoppingToken) { // 创建临时作用域 using var scope = _serviceProvider.CreateScope(); var tokenAcquisition = scope.ServiceProvider.GetRequiredService<ITokenAcquisition>(); // 在作用域内使用服务 var token = await tokenAcquisition.GetAccessTokenForAppAsync("api://xxxxx-5a7f-430e-8ea1-6e133055990e/.default"); while (!stoppingToken.IsCancellationRequested) { Console.WriteLine("Executing..."); await Task.Delay(1000, stoppingToken); } } }
2. 为何代码在Test/Production模式下可行,但Development模式下失效?
这是因为.NET在不同环境下的服务验证策略不同:
- Development环境:默认启用
ValidateScopes和ValidateOnBuild选项,启动时会严格检查服务的生命周期依赖关系。当检测到单例依赖Scoped服务时,直接抛出异常阻止应用启动。 - Production/Test环境:默认关闭
ValidateScopes验证,不会主动检查这种不合法的依赖,所以应用能启动。但这只是“表面可行”,实际上潜在的生命周期问题依然存在,可能在运行时引发难以排查的BUG。
如果需要统一各环境的验证行为,可以在Program.cs中显式配置:
var builder = WebApplication.CreateBuilder(args); // 强制开启服务验证(所有环境) builder.Host.UseDefaultServiceProvider(options => { options.ValidateScopes = true; options.ValidateOnBuild = true; });
内容的提问来源于stack exchange,提问作者David Mason
相关产品推荐
相关产品推荐

