在启动后执行异步代码,一旦API为请求启动



我正试图在启动我的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,注册StartupTaskRunnerYourStartupTask:

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>();
}
}

指出
应用程序启动顺序为:

  1. StartHostedService's
  2. 启动Kesterl服务器
  3. 火灾ApplicationStarted事件

如果您想在HostedService启动之前运行启动任务您需要确保StartupTasksRunner在任何其他HostedService之前注册(它们按注册顺序启动)。

如果您想在所有HostedServices启动后运行启动任务但是在应用程序开始接收web请求之前请确保您在注册HostedService之后注册StartupTasksRunner

如果你想在Kestrel Server启动后运行启动任务取消注释// await _semaphore.WaitAsync(cancellationToken);行。请注意,它可能与web请求处理并发运行。

还要注意IHostedService.StartAsync方法抛出的异常将被框架吞噬。因此,如果成功执行启动任务对您的应用程序是必要的,取消注释// _applicationLifetime.StopApplication();

相关内容

最新更新