GenericHost/DefaultHost/WebHost 生态系统变化迅速,并且似乎缺乏需求组合。我正在使用 .NET 8.0。
使用截至 2024 年 1 月的稳定包,创建应用程序主机、配置日志记录/DI/配置并等待单个应用程序循环退出,同时提供 Ctrl+C 支持以提前优雅终止的控制台应用程序是什么样的?
使用以下代码,我在控制台上得到“RunAsync done”,然后是“stopped”,并正常关闭。如果我尝试使用 Ctrl+C 打破假“工作循环”,应用程序会立即退出,不会正常终止。
public class Program
{
public static async Task Main(string[] args)
{
var hostBuilder = CreateDefaultApp(args);
var host = hostBuilder.Build();
Console.WriteLine("starting");
var app = host.Services.GetRequiredService<TestApp>();
await app.RunAsync();
Console.WriteLine("stopped");
}
private static IHostBuilder CreateDefaultApp(string[] args)
{
var builder = Host.CreateDefaultBuilder();
builder.ConfigureServices(conf =>
{
conf.AddSingleton<TestApp>();
});
builder.ConfigureLogging(conf =>
{
conf.ClearProviders();
conf.AddConsole();
});
builder.UseConsoleLifetime();
return builder;
}
}
public class TestApp
{
private readonly IHostLifetime _lifetime;
private readonly IHostApplicationLifetime _appLifetime;
public TestApp(IHostLifetime lifetime, IHostApplicationLifetime appLifetime)
{
_lifetime = lifetime;
_appLifetime = appLifetime;
}
public async Task RunAsync()
{
var stopToken = _appLifetime.ApplicationStopping;
Console.WriteLine("RunAsync starting");
for (int i = 0; i < 10; i++)
{
if (stopToken.IsCancellationRequested)
{
Console.WriteLine("STOP");
break;
}
await Task.Delay(250);
Console.Write(i);
}
Console.WriteLine();
Console.WriteLine("RunAsync done");
}
}
我不在乎我的应用程序逻辑是否存在于这个有点独立的“TestApp”类中,或者作为托管服务运行,只要它可以注入依赖项,可以触发它自己的优雅(和不优雅)退出,运行在一个
async
上下文,并且可以使用 Ctrl+C 相对优雅地提前终止。我想使用 Microsoft 的 Host
提供程序来完成大部分配置,并且我不想“简单地”完成我自己的所有依赖项管理和配置。
您可以使用后台工作人员(请参阅.NET 中的工作人员服务)来实现您想要的行为。
我稍微修改了你的代码。请注意,您需要将项目模板更改为
Microsoft.NET.Sdk.Worker
public class Program
{
public static async Task Main(string[] args)
{
var host = CreateDefaultApp(args).Build();
Console.WriteLine("starting");
await host.RunAsync();
Console.WriteLine("stopped");
}
private static IHostBuilder CreateDefaultApp(string[] args)
{
var builder = Host.CreateDefaultBuilder();
builder.ConfigureServices(conf =>
{
conf.AddHostedService<Worker>();
});
builder.ConfigureLogging(conf =>
{
conf.ClearProviders();
conf.AddConsole();
});
return builder;
}
}
public class Worker : BackgroundService
{
private readonly IHostApplicationLifetime _HostApplicationLifetime;
private readonly ILogger<Worker> _Logger;
public Worker(IHostApplicationLifetime hostApplicationLifetime, ILogger<Worker> logger)
{
_HostApplicationLifetime = hostApplicationLifetime;
_Logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Console.WriteLine("RunAsync starting");
for (int i = 0; i < 10; i++)
{
if (stoppingToken.IsCancellationRequested)
{
Console.WriteLine("STOP");
break;
}
await Task.Delay(250);
Console.Write(i);
}
_Logger.LogInformation("test logging");
Console.WriteLine();
Console.WriteLine("RunAsync done");
_HostApplicationLifetime.StopApplication();
}
}