.NET6 WebApi向Timer后台服务注入接口报错问题
.NET6 WebApi中BackgroundService注入Scoped服务报错的解决方案
问题现象
在.NET6 WebApi项目中,向基于BackgroundService实现的定时服务AddressCheck注入IInvoiceRepository接口时启动报错,但该接口注入到继承ControllerBase的控制器时无异常。
终端报错信息
Unhandled exception. System.AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: API.Services.AddressCheck': Cannot consume scoped service 'API.Interfaces.IInvoiceRepository' from singleton 'Microsoft.Extensions.Hosting.IHostedService'.) ---> System.InvalidOperationException: Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: API.Services.AddressCheck': Cannot consume scoped service 'API.Interfaces.IInvoiceRepository' from singleton 'Microsoft.Extensions.Hosting.IHostedService'.
问题原因
BackgroundService通过AddHostedService注册后,生命周期为Singleton(单例);而IInvoiceRepository默认注册为Scoped(作用域)服务(控制器能正常注入正是因为控制器本身是Scoped生命周期)。
在.NET依赖注入体系中,Singleton服务不能直接依赖Scoped服务——Scoped服务的生命周期绑定到请求作用域,会在请求结束后被释放,而Singleton服务会长期存在,直接依赖会导致Scoped服务被提前释放或引发线程安全问题。
解决方案
方案一:使用IServiceScopeFactory创建临时作用域(推荐)
通过注入IServiceScopeFactory,在每次定时任务执行时创建独立的作用域,从作用域中获取IInvoiceRepository实例,确保每次使用的都是符合生命周期的服务实例。
修改后的AddressCheck代码:
namespace API.Services { public class AddressCheck : BackgroundService { private readonly PeriodicTimer _timer = new(TimeSpan.FromSeconds(5000)); private readonly ILogger<AddressCheck> _logger; private readonly IServiceScopeFactory _scopeFactory; public AddressCheck(ILogger<AddressCheck> logger, IServiceScopeFactory scopeFactory) { _logger = logger; _scopeFactory = scopeFactory; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (await _timer.WaitForNextTickAsync(stoppingToken) && !stoppingToken.IsCancellationRequested) { await CheckList(stoppingToken); } } public async Task CheckList(CancellationToken stoppingToken) { // 创建临时作用域 using var scope = _scopeFactory.CreateScope(); var invoiceRepository = scope.ServiceProvider.GetRequiredService<IInvoiceRepository>(); // 执行仓储操作 var addressList = invoiceRepository.GetListofAddress(); // 后续业务逻辑... } } }
方案二:将IInvoiceRepository改为Singleton生命周期(不推荐)
如果IInvoiceRepository的实现是无状态且线程安全的,可以修改其注册方式为Singleton:
// 在Program.cs或Startup.cs的服务配置中 services.AddSingleton<IInvoiceRepository, InvoiceRepository>();
注意:如果仓储依赖了其他Scoped服务(比如
DbContext),此方案会导致依赖的Scoped服务长期存在,引发线程安全问题和内存泄漏,因此不推荐。
内容的提问来源于stack exchange,提问作者Jet813
相关产品推荐
相关产品推荐

