I'm trying to execute logic on start up of my web API and this code will also be async.
I've thought of IStartupFilter
but this is sync, which is a problem. StartUp.Configure()
is also not an option, sync.
I've thought of IHostedService
but this runs before the application is done loading and up for requests.
Any other ways of doing this ?
CodePudding user response:
Something you probably didn't realize is that running the application has 2 parts, that are hidden by default when you call IHost.Run
/IHost.RunAsync
.
Basically, this:
var host = CreateHostBuilder(args).Build();
await host.RunAsync();
Is equivalent to this:
var host = CreateHostBuilder(args).Build();
await host.StartAsync();
await host.WaitForShutdownAsync();
When StartAsync
returns, the application has already started and is ready to consume requests, so you probably want to do this:
var host = CreateHostBuilder(args).Build();
await host.StartAsync();
await PerformSomeWorkAfterStartupAsync();
await host.WaitForShutdownAsync();
CodePudding user response:
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();
}
internal class StartupTasksRunner : IHostedService
{
private readonly ILogger<StartupTasksRunner> _logger;
private readonly IEnumerable<IAsyncStartupTask> _startupTasks;
public StartupTasksRunner(ILogger<StartupTasksRunner> logger, IEnumerable<IAsyncStartupTask> startupTasks)
{
_logger = logger;
_startupTasks = startupTasks;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
foreach (var task in _startupTasks)
{
var stopwatch = Stopwatch.StartNew();
try
{
await task.OnStartupAsync().ConfigureAwait(false);
}
catch (Exception e)
{
var seconds = stopwatch.Elapsed.TotalSeconds;
_logger.LogError(e, $"Startup task '{task.GetType().Name}' failed after {seconds:0.###} sec");
throw;
}
finally
{
var seconds = stopwatch.Elapsed.TotalSeconds;
_logger.LogInformation($"Startup task '{task.GetType().Name}' execution time: {seconds:0.###} sec");
}
}
}
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>();
}
}