.NET 8.0 WPF中正确使用.NET Generic Host与IHostApplicationLifetime
我希望在.NET 8.0 WPF应用中使用.NET Generic Host,并借助IHostApplicationLifetime执行自定义的启动初始化与销毁清理操作。网上找到的方案仅将宿主用作DI容器,未调用host.Start()/host.StartAsync(),导致注入的IHostedService无法触发IHostApplicationLifetime的事件。
原方案代码:
Program.cs
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.DependencyInjection; using System; public class Program { [STAThread] public static void Main() { var host = Host.CreateDefaultBuilder() .ConfigureServices(services => { services.AddSingleton<App>(); services.AddSingleton<MainWindow>(); // 注入自定义HostedService但无法触发生命周期事件 services.AddHostedService<MyHostedService>(); }) .Build(); var app = host.Services.GetService<App>(); app?.Run(); } }
App.cs
using System.Windows; public class App : Application { readonly MainWindow mainWindow; public App(MainWindow mainWindow) { this.mainWindow = mainWindow; } protected override void OnStartup(StartupEventArgs e) { mainWindow.Show(); base.OnStartup(e); } }
MyHostedService.cs
public class MyHostedService : IHostedService { public MyHostedService(IHostApplicationLifetime appLifetime) { appLifetime.ApplicationStarted.Register(OnStarted); appLifetime.ApplicationStopping.Register(OnStopping); } public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask; public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; private void OnStarted() { // 自定义启动逻辑 } private void OnStopping() { // 自定义销毁逻辑 } }
在app?.Run()前添加host.Start()后功能正常,但不确定是否为正确实现,同时希望将MyHostedService复用在控制台应用中,捕获WPF与控制台应用的启动/关闭时机。
1. 同步启动Host的正确方式
你添加host.Start()的做法是正确的,因为Generic Host必须通过Start()或StartAsync()启动,才能激活所有注册的IHostedService并触发IHostApplicationLifetime的生命周期事件。
需要注意应用退出时要确保Host正常停止,完整的Program.cs代码如下:
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.DependencyInjection; using System; public class Program { [STAThread] public static void Main() { using var host = Host.CreateDefaultBuilder() .ConfigureServices(services => { services.AddSingleton<App>(); services.AddSingleton<MainWindow>(); services.AddHostedService<MyHostedService>(); }) .Build(); // 启动Host,激活HostedService并触发生命周期 host.Start(); try { var app = host.Services.GetRequiredService<App>(); app.Run(); } finally { // 应用退出时停止Host,触发ApplicationStopping等事件 host.StopAsync().GetAwaiter().GetResult(); } } }
2. 更规范的异步启动方案
Generic Host的设计优先支持异步操作,你可以将Main方法改为异步版本(注意保留[STAThread]):
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.DependencyInjection; using System; using System.Threading.Tasks; public class Program { [STAThread] public static void Main() => MainAsync().GetAwaiter().GetResult(); private static async Task MainAsync() { using var host = Host.CreateDefaultBuilder() .ConfigureServices(services => { services.AddSingleton<App>(); services.AddSingleton<MainWindow>(); services.AddHostedService<MyHostedService>(); }) .Build(); await host.StartAsync(); try { var app = host.Services.GetRequiredService<App>(); // 用Dispatcher等待WPF应用退出 await app.Dispatcher.InvokeAsync(app.Run); } finally { await host.StopAsync(); } } }
3. 跨应用复用MyHostedService的实现
MyHostedService的设计已经具备跨应用复用的能力,因为它依赖的IHostApplicationLifetime是Generic Host的核心服务,无论是WPF还是控制台应用,只要基于Generic Host构建,都能自动提供该服务。
控制台应用中的复用示例
只需在控制台应用的Host配置中注入MyHostedService即可:
using Microsoft.Extensions.Hosting; public class ConsoleProgram { public static async Task Main(string[] args) { using var host = Host.CreateDefaultBuilder(args) .ConfigureServices(services => { services.AddHostedService<MyHostedService>(); // 其他控制台服务注册 }) .Build(); await host.RunAsync(); } }
控制台应用中host.RunAsync()会自动处理Host的启动和停止,MyHostedService的OnStarted和OnStopping会在对应时机触发。
内容的提问来源于stack exchange,提问作者bairog

