My .net core app needs to crawl data in a specified time interval. I have chosen to implement IHostedService
to run it in parallel with API. The hosted service needs some services injected. I register them in startup.cs
, but I get an error:
System.InvalidOperationException: 'Cannot consume scoped service 'IXService' from singleton 'Microsoft.AspNetCore.Hosting.Internal.HostedServiceExecutor'.'
My startup.cs:
services.AddScoped<IXService, XService>();
services.AddHostedService<MyHostedService>();
I had a similar problem yet with DbContext, I solved it with https://stackoverflow.com/a/48368934/8475133, but this time I need dependency injection going through deeper layers and dealing with IServiceScopeFactory in each doesn't seem to be an elegant solution.
Singleton is a single instance for the lifetime of the application domain. Scoped is a single instance for the duration of the scoped request, which means per HTTP request in ASP.NET. Transient is a single instance per code request.
Use Transient lifetime for the lightweight service with little or no state. Scoped services service is the better option when you want to maintain state within a request. Singletons are created only once and not destroyed until the end of the Application. Any memory leaks in these services will build up over time.
When you register implementations of IHostedService using any of the AddHostedService extension methods - the service is registered as a singleton.
The reason you're not allowed to do this is because MyHostedService has a singleton lifetime, which is a longer lifetime than scoped. The basic assumption here is that a service that is registered as scoped should not be kept alive indefinitely, this could easily happen if a singleton service keeps a reference to a scoped service.
I think the solution you're looking for is to inject IServiceProvider into MyHostedService, use it to create a new scope and new XService instance whenever you need it.
That is, replace
_xService.Foo();
with
using(var scope = _serviceProvider.CreateScope()) {
var xService = scope.ServiceProvider.GetService<IXService>();
xService.Foo();
}
An alternative, of course, would be to simply register XService as a singleton, by just replacing the call to AddScoped with AddSingleton, but I would not recommend it.
Edit: I must admit to not reading your link before posting my response. However, I still think this is the most elegant solution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With