Search code examples
c#asp.net-coreasp.net-core-3.0asp.net-core-hosted-services

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?


Solution

  • 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.