Registering IHostedService on a different service provider

Viewed 135

Is it possible to run an IHostedService on a service provider that is not the one provided by ConfigureServices?

I am working on a library that helps configure various environments and each one has its own context (the environment context is driven by service providers). I can call .AddHostedService<>(); just fine, it doesn't throw, but the service constructor never gets called and the service doesn't start.

Edit:

I can manually kick off the services like Andy suggests in the answers, but where would that happen?

2 Answers

All IHostedService implementations are executed by the IWebHost that holds on to the IServiceProvider which is passed in to ConfigureServices.

The internal WebHost object holds on to an internal HostedServiceExecutor that is responsible for instantiating and running IHostedService objects.

Since you are running outside of the WebHost, you are the one responsible for instantiating, starting and stopping the services.

What you would have to do is emulate what HostedServiceExecutor does: Instantiate then iterate all services that implement IHostedService and finally call StartAsync and StopAsync

For example:

var serviceCollection = new ServiceCollection();
serviceCollection.AddHostedService<TestHostedService>();
var serviceProvider = serviceCollection.BuildServiceProvider();

// this will instantiate the services
var myHostedServices = serviceProvider.GetService<IEnumerable<IHostedService>>();
// or: var myHostedServices = serviceProvider.GetServices<IHostedService>();

// start the services up
foreach (var hostedService in myHostedServices)
{
    await hostedService.StartAsync(token);
}

// ...

// Shutting down: stop the services
foreach (var hostedService in myHostedServices)
{
    await hostedService.StopAsync(token);
}

This is extremely simplified code. No exception handling is done. This is just to give you the gist of how you'd do it. You should really study HostedServiceExecutor's code.

As alternative to Andy's suggested solution, you can make the IHostedService into an adapter that forward the call to its own private IServiceProvider:

public class IsolatedHostedService : IHostedService
{
    private IServiceProvider provider;

    // Functions as Composition Root
    public IsolatedHostedService()
    {
        var services = new ServiceCollection();
        services.AddXXX();
        provider = services.BuildServiceProvider(validateScopes: true);
    }

    public Task StartAsync(CancellationToken t)
    {
        provider.GetRequiredService<YourStuff>().DoSomething();
    }
}
Related