Implement IStartupTask
, register StartupTaskRunner
and YourStartupTask
in DI:
services
.AddStartupTasksRunner()
.AddStartupTask<YourStartupTask>();
And here the code based on Andrew's Lock post:
public interface IAsyncStartupTask
{
Task OnStartupAsync(CancellationToken cancellationToken);
}
internal class StartupTasksRunner : IHostedService
{
private readonly IEnumerable<IAsyncStartupTask> _startupTasks;
private readonly IHostApplicationLifetime _applicationLifetime;
private readonly SemaphoreSlim _semaphore;
public StartupTasksRunner(IEnumerable<IAsyncStartupTask> startupTasks, IHostApplicationLifetime applicationLifetime)
{
_startupTasks = startupTasks;
_applicationLifetime = applicationLifetime;
_semaphore = new SemaphoreSlim(0);
applicationLifetime.ApplicationStarted.Register(() => _semaphore.Release());
}
public async Task StartAsync(CancellationToken cancellationToken)
{
// wait for ApplicationStarted event to execute when app is listening to web requests
// await _semaphore.WaitAsync(cancellationToken);
foreach (var task in _startupTasks)
{
try
{
await task.OnStartupAsync(cancellationToken).ConfigureAwait(false);
}
catch
{
// stop the application if failing startup task if fatal
//_applicationLifetime.StopApplication();
throw;
}
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddStartupTasksRunner(this IServiceCollection services)
{
return services.AddHostedService<StartupTasksRunner>();
}
public static IServiceCollection AddStartupTask<TStartupTask>(this IServiceCollection services)
where TStartupTask : class, IAsyncStartupTask
{
return services.AddSingleton<IAsyncStartupTask, TStartupTask>();
}
}
Notes
The app startup order is:
- Start
HostedService
's
- Start Kesterl Server
- Fire
ApplicationStarted
event
If you want to run your startup task before HostedService
s started you need to make sure StartupTasksRunner
registered before any other HostedService
(they are started in registration order).
If you want to run your startup task after all HostedService
s started but before application starts to receive web requests make sure you are registering StartupTasksRunner
after any other HostedService
.
If you want to run your startup task after Kestrel Server is started uncomment // await _semaphore.WaitAsync(cancellationToken);
line. Be aware that it may run concurrently with web requests handling.
Also be aware that exceptions thrown by IHostedService.StartAsync
method will be swallowed by framework. So if successful execution of startup tasks is necessary for your application uncomment // _applicationLifetime.StopApplication();