Reputation: 2073
I know prior to Asp.Net 3.0 (or 3.1), to manually start a BackgroundService, we could derive it from IHostedService
instead and change the DI registration to:
services.AddSingleton<IHostedService, CustomHostedService>();
and then manually trigger the service start by injecting the service in the constructor, and calling StartAsync()
.
However, I can't seem to do that in Asp.Net Core 3.1. Having a look at the code for the StartAsync()
method, background services are started before the app startup is completed.
public async Task StartAsync(CancellationToken cancellationToken = default)
{
_logger.Starting();
await _hostLifetime.WaitForStartAsync(cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
_hostedServices = Services.GetService<IEnumerable<IHostedService>>();
foreach (var hostedService in _hostedServices)
{
// Fire IHostedService.Start
await hostedService.StartAsync(cancellationToken).ConfigureAwait(false);
}
// Fire IApplicationLifetime.Started
_applicationLifetime?.NotifyStarted();
_logger.Started();
}
What's the best way to manually trigger a background service to start?
Essentially, this is my setup:
Background Service
public MyBackgroundService(Func<string, IConsumer> consumerFactory)
{
_consumerFactory = consumerFactory ?? throw new ArgumentNullException(nameof(consumerFactory));
}
Consumer factory registration
services.AddSingleton<Func<string, IConsumer>>(provider => providerName => provider.ConfigureConsumer(environment, providerName));
private static IConsumer ConfigureConsumer(this IServiceProvider provider, IHostEnvironment environment, string provideName)
{
if (string.IsNullOrEmpty(provideName))
throw new ArgumentNullException(nameof(provideName));
var options = provider.GetRequiredService<IOptions<ConsumerConfig>>();
if (options?.Value == null)
throw new ArgumentNullException(nameof(options));
return environment.IsDevelopment()
? new Consumer(options, provider.GetTopics())
: new Consumer((IOptions<ConsumerConfig>)options.SetSecurityOptions(provider), provider.GetTopics());
}
private static IOptions<Config> SetSecurityOptions(this IOptions<Config> config, IServiceProvider provider)
{
var certService = provider.GetRequiredService<IVaultCertificateService>();
...
return config;
}
Essentially this certService properties are only set when a request comes in and executes a middleware. And since when ExecuteAsync()
in the background service tries to get an instance of consumer from the factory, is executed regardless, I dont have the IConsumer properly configured
Thanks
Upvotes: 7
Views: 13258
Reputation: 346
Injected Hosted services start with the app and stops with it. Framework doesn't provide you manual control. There should be underlying reasons for that like. Of course, you can implement your own solutions with threads but if your wish to go on with Microsoft's built-in solutions you can use Queued Background Task. You will start a background service with an empty task queue. Service will be listening to your first task to be added to the queue. You can add a task whenever you wish then it will be running at background.
Upvotes: 8