如何确定在关闭时调用 IHostedServices 的顺序?

How to determine in which order IHostedServices are being called at shutdown?

我正在维护一个集成包,允许我的用户将我的库与 ASP.NET Core 集成。此软件包必须与从 2.1 开始的所有 ASP.NET Core 版本兼容。在应用程序关闭时,我的集成包必须能够执行异步清理,不幸的是不能依赖 IAsyncDisposableMicrosoft.Bcl.AsyncInterfaces(见下文)。

因此,唯一可行的方法是注册一个 IHostedService 实现。它的 StopAsync 方法在关闭时被调用:

public sealed class ShutdownHostedService : IHostedService
{
    public MyLibaryCleanupObject Obj;
    public Task StartAsync(CancellationToken token) => Task.CompletedTask;
    public Task StopAsync(CancellationToken token) => this.Obj.CleanupAsync();
}

services.AddSingleton<IHostedService>(new ShutdownHostedService { Obj = ... });

但是,应用程序开发人员当然可以添加他们自己的 IHostedService 实现,这可能会与我的库交互。这就是为什么最后调用我自己的 IHostedService 实现很重要。但问题就出在这里。

引入 ASP.NET Core 2.1 应用程序开发人员可以选择使用新的 Microsoft.Extensions.Hosting.Host 还是(现已弃用)Microsoft.AspNetCore.WebHost。对于 WebHost,在关闭时,IHostedService 实现按注册顺序调用,而对于 HostIHostedService 实现在 中调用注册的相反顺序

这对我来说是有问题的,因为我的托管服务应该最后调用。由于应用程序开发人员可能会在他们现有的 ASP.NET 核心应用程序中使用我的集成包,他们可能仍会使用 WebHost,这就是为什么支持该场景很重要。

问题:确定 'mode' ASP.NET 核心应用程序运行的可靠方法是什么,以便我可以决定首先或最后添加我的托管服务?

或者,为了防止陷入 XY 问题陷阱,我愿意接受完全不同的解决方案来解决我实施“异步关闭”的问题。

关于 IAsyncDisposable 的注释:

我想到的一个解决方案(正如 Ian 在评论中正确指出的那样)是向 ServiceCollection 添加 IAsyncDisposable 单例注册。这将允许在关闭时进行异步清理。不幸的是,由于限制(已解释 here),我的集成包不可能依赖 Microsoft.Bcl.AsyncInterfaces,因此不会依赖 IAsyncDisposable。这是一个不幸的情况,肯定会使事情复杂化。事实上,无法依赖 IAsyncDisposable 的原因是我正在寻找实现异步关闭代码的替代方法。

我最终使用的解决方案与 Microsoft Microsoft.Extensions.Hosting.Internal.Host class 选择的解决方案相同,并且不涉及使用 IHostedService 个实例。 Host class 包含以下 Dispose 方法:

public void Dispose()
{
    this.DisposeAsync().GetAwaiter().GetResult();
}

这可能会引起一些人的注意,并可能被视为一种不好的做法,但请不要忘记:

  • 此代码保证 运行 在 ASP.NET 核心的上下文中,此代码不会导致死锁
  • 此代码 运行 仅在关机时出现一次,因此性能在这里不是问题。

这就是微软 Host class 可以采用这种方法的原因,这意味着我的库也是一种安全的方法。