.net core dependency injection to hosted service [duplicate]
Asked Answered
R

1

10

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://mcmap.net/q/144128/-how-to-consume-a-scoped-service-from-a-singleton, 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.

Reservist answered 25/8, 2018 at 19:58 Comment(3)
I don't get it. If you could resolve the same problem for DbContext why couldn't you resolve it for the rest of the services MyHostedService needs?Katheleenkatherin
"...doesn't seem to be elegant solution", Suppose i have many services to inject deep down, each consume in constructor some other services, this solution results in additional lines of code with usings, while standard dependency injection is just couple of lines with registering services and then including them in parameters of constructor it is much more clear.Reservist
Yet you accepted the answer that is a duplicate from the link you posted on the question?Katheleenkatherin
N
17

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.

Nonlegal answered 25/8, 2018 at 20:27 Comment(3)
This not completely correct. AddHostedService<T> is adding them as transient but they are only created once by the HostedServiceExecutor learn.microsoft.com/en-us/dotnet/api/….Superstitious
Thank you for the correction, I misread the error message. It clearly says HostedServiceExecutor.Produce
Just to update. In Nov of 2018, the code was changed to do a register as singleton which is very important. It allows other requests to retrieve the same hosted service as a singleton. See AspNetCore/#4147 make AddHostedService register singletons on github github.com/dotnet/extensions/commit/…Gallice

© 2022 - 2024 — McMap. All rights reserved.