我正试图在启动我的web API时执行逻辑,此代码也将是异步的。
我想过IStartupFilter
,但这是同步的,这是一个问题。StartUp.Configure()
也不是一个选项,sync.
我想到了IHostedService
,但这在应用程序完成加载和请求之前运行。
还有其他方法吗?
您可能没有意识到的是,运行应用程序有两个部分,这是默认情况下隐藏的,当您调用IHost.Run
/IHost.RunAsync
。
基本上是这样的:
var host = CreateHostBuilder(args).Build();
await host.RunAsync();
等价于:
var host = CreateHostBuilder(args).Build();
await host.StartAsync();
await host.WaitForShutdownAsync();
当StartAsync
返回时,应用程序已经启动并准备好接收请求,因此您可能希望这样做:
var host = CreateHostBuilder(args).Build();
await host.StartAsync();
await PerformSomeWorkAfterStartupAsync();
await host.WaitForShutdownAsync();
在DI中实现IStartupTask
,注册StartupTaskRunner
和YourStartupTask
:
services
.AddStartupTasksRunner()
.AddStartupTask<YourStartupTask>();
下面是基于Andrew的Lock帖子的代码:
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>();
}
}
指出
应用程序启动顺序为:
- Start
HostedService
's - 启动Kesterl服务器
- 火灾
ApplicationStarted
事件
如果您想在HostedService
启动之前运行启动任务您需要确保StartupTasksRunner
在任何其他HostedService
之前注册(它们按注册顺序启动)。
如果您想在所有HostedService
s启动后运行启动任务但是在应用程序开始接收web请求之前请确保您在注册HostedService
之后注册StartupTasksRunner
。
如果你想在Kestrel Server启动后运行启动任务取消注释// await _semaphore.WaitAsync(cancellationToken);
行。请注意,它可能与web请求处理并发运行。
还要注意IHostedService.StartAsync
方法抛出的异常将被框架吞噬。因此,如果成功执行启动任务对您的应用程序是必要的,取消注释// _applicationLifetime.StopApplication();