Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to reference a hosted service in .Net Core 3?

Back in .net core 2, I had created a hosted service with a custom property like:

 public class MyService : BackgroundService 
{
public bool IsRunning {get;set;}
...

That I could setup in startup.cs like:

public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHostedService,HostedServices.MyService>();
...

And then I could reference it elsewhere in a razor page like:

public class IndexModel : PageModel
{
    private readonly IHostedService _mySrv;
    public IndexModel(IHostedService mySrv) => _mySrv = mySrv;

    [BindProperty]
    public bool IsRunning { get; set; }

    public void OnGet() => IsRunning = ((HostedServices.MyService)_mySrv).IsRunning;
}

Now that I've upgraded to .net core 3, my startup has changed to:

services.AddHostedService<HostedServices.MyService>();

But my DI reference in IndexModel doesn't get me my MyService anymore, it gives me an object of type GenericWebHostService instead, that I can't figure out how to get my custom MyService from. Changing 'IHostedService' to 'MyService' in IndexModel doesn't work either, i get a 'Unable to resolve service' error.

How do I get an instance of MyService back from dependency injection?

like image 792
Phil Avatar asked Oct 27 '19 17:10

Phil


People also ask

What is a hosted service .NET Core?

In ASP.NET Core, background tasks can be implemented as hosted services. A hosted service is a class with background task logic that implements the IHostedService interface. This article provides three hosted service examples: Background task that runs on a timer. Hosted service that activates a scoped service.

Is IHostedService a singleton?

When you register implementations of IHostedService using any of the AddHostedService extension methods - the service is registered as a singleton. There may be scenarios where you'd like to rely on a scoped service. For more information, see Dependency injection in .


1 Answers

In 2.2, the setup you had worked mostly by chance. Whenever you register multiple implementations against a service, the last-registered is the one that "wins". For example, take the following code:

services.AddSingleton<IHostedService, HostedService1>();
services.AddSingleton<IHostedService, HostedService2>();

// ...

public IndexModel(IHostedServie hostedService) { }

The implementation of IHostedService that gets injected into IndexModel is HostedService2; the last registered. If IndexModel were to be updated to take an IEnumerable<IHostedService>, it would get both implementations, in order of registration:

public IndexModel(IEnumerable<IHostedService> hostedServices) { }

When I said "by chance", I meant that in your example, only HostedServices.MyService gets registered, so it's also the last-registered and therefore it "wins".

In 3.0, when using the Generic Host, an implementation of IHostedService, GenericWebHostService, handles processing web requests. This gives you a problem, because the GenericWebHostService is registered after HostedServices.MyService. I hope it's clear by now that this is the reason why the IHostedService you request in IndexModel is not what you expected.

In terms of a solution, I suggest performing two registrations:

services.AddSingleton<HostedServices.MyService>();
services.AddHostedService(sp => sp.GetRequiredService<HostedServices.MyService>());

Then, update your IndexModel to require your specific implementation:

public IndexModel(HostedServices.MyService myService) { }

This allows you to target your specific implementation of IHostedService. It's registered twice, against two different service types, but only one instance gets created.

like image 153
Kirk Larkin Avatar answered Nov 09 '22 12:11

Kirk Larkin