前言
上次,我们介绍了《如何获取 ASP.NET Core 启动地址》。
但是,如果要在 BackgroundService 中获取启动地址可不那么容易,因为 BackgroundService 在 app 启动前就开始执行了:.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHostedService<DemoBackgroundService>();
var app = builder.Build();
app.MapGet("/", () => "Hello My IO!");
await app.StartAsync();
Console.WriteLine(app.Urls.First());
await app.WaitForShutdownAsync();
internal class DemoBackgroundService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Console.WriteLine("BackgroundService ExecuteAsync");
await Task.CompletedTask;
}
}
那要怎么实现呢?
IHostApplicationLifetime
我们可以利用ASP.NET Core应用程序生存期事件,等待启动完成再获取启动地址:
internal class DemoBackgroundService : BackgroundService
{
private readonly IServiceProvider _services;
private string _url;
public DemoBackgroundService(IServiceProvider services, IHostApplicationLifetime lifetime)
{
_services = services;
lifetime.ApplicationStarted.Register(OnAppStarted);
}
public void OnAppStarted()
{
var server = _services.GetService<IServer>();
_url = server.Features.Get<IServerAddressesFeature>().Addresses.First();
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (string.IsNullOrWhiteSpace(_url))
{
await Task.Delay(1000);
}
Console.WriteLine("BackgroundService ExecuteAsync");
Console.WriteLine(_url);
await Task.CompletedTask;
}
}
现在可以正常获取 ASP.NET Core 启动地址了:
SemaphoreSlim
轮询虽然可以解决问题,但感觉不太优雅。
我们可以创建一个信号灯 SemaphoreSlim,在ExecuteAsync
方法中等待信号灯,而在启动后释放信号灯:
static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(0, 1);
public void OnAppStarted()
{
...
semaphoreSlim.Release();
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await semaphoreSlim.WaitAsync();
...
}
可以看到,这样还带来了另外一个好处,比上面的解决方式更快地进入到了 BackgroundService:
结论
今天,我们利用了ASP.NET Core应用程序生存期事件,实现了在 BackgroundService 获取 ASP.NET Core 启动地址。