I've got a problem in my app where two IHostedService
instances are started (StartAsync()
) in the wrong order. One takes a dependency on the other (but not in the constructor, but later in another class through a IServiceScopeFactory
).
Let's say there are services A and B.
Service B needs service A, so it will (somewhere down the class hierarchy) get an instance of service A and call a method on it. But service A can only be used after it has been started. Now the ASP.NET Core (2.2) app starts service B first, then service A. That leads to several errors in the code logic because use-before-start is bad and start-after-use is also bad.
Service A needs a database connection to initialise its state, which I assume is only available in the StartAsync
method, not already in the constructor.
Here's the code skeleton:
class ServiceA : IHostedService
{
public Task StartAsync(CancellationToken cancellationToken)
{
// Load working state from the database.
// This may take a moment depending on the amount of data to read.
}
public void DoSomething(object data)
{
// Use the working state and decide whether to add a database record.
// Will fail if the database record already exists but the working
// state doesn't reflect that yet because it wasn't loaded.
// This method should not be async.
}
}
class ServiceB : IHostedService
{
public Task StartAsync(CancellationToken cancellationToken)
{
// Start other code which will eventually call
// ServiceA.DoSomething before it was fully started.
}
}
// ASP.NET Core Startup class
class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// The order doesn't matter here
services.AddSingletonHostedService<ServiceA>();
services.AddSingletonHostedService<ServiceB>();
}
}
// Extension method
public static IServiceCollection AddSingletonHostedService<TService>(this IServiceCollection services)
where TService : class, IHostedService
{
services.AddSingleton<TService>();
services.AddSingleton<IHostedService, BackgroundServiceStarter<TService>>();
return services;
}
For BackgroundServiceStarter see https://stackoverflow.com/a/51584615. This is required to make IHostedService
services available through dependency injection which wasn't thought of by Microsoft.
How can I tell the dependency container that it should start service A before starting service B (and wait for it)?
If that isn't possible, I'll have to let uses of service A before it has been started block until is has been started. I'm not sure if this will cause problems with a potentially large number of blocked threads (the API of service A is not async and doesn't have to be). There should be no circular dependency so it shouldn't dead-lock, but you never know what will be added later.