首页 > 解决方案 > 如何确定在关机时调用 IHostedServices 的顺序?

问题描述

我正在维护一个集成包,它允许我的用户将我的库与 ASP.NET Core 集成。此包必须与从 2.1 开始的所有 ASP.NET Core 版本兼容。在应用程序关闭时,我的集成包必须能够执行异步清理,不幸的是不能依赖IAsyncDisposablethrough Microsoft.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。With WebHost, 在关闭时,IHostedService实现按注册顺序调用,而 with HostIHostedService实现按注册顺序相反的顺序调用。

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

问题:确定 ASP.NET Core 应用程序在什么“模式”下运行的可靠方法是什么,以便我可以决定首先或最后添加我的托管服务?

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

注意事项IAsyncDisposable

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

标签: c#asp.net-coreapplication-shutdownasp.net-core-hosted-services

解决方案


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

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

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

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

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


推荐阅读