如何在同一应用中托管WebApplication与BackgroundService
我用dotnet new worker创建了一个命令行应用,用来托管BackgroundService实现,代码如下:
var host = Host.CreateDefaultBuilder(args) .ConfigureServices(services => { services.AddHostedService<MyBackgroundService>(); services.AddSingleton<IMyType>(myinstance); // 其他服务配置... }).Build(); await host.RunAsync();
现在我想让这个应用同时托管Web API,目前我是在MyBackgroundService类里实例化了独立的WebApplicationBuilder,用的是另一套独立的服务、单例等资源:
var builder = WebApplication.CreateBuilder(); // Web相关配置... var webApi = builder.Build(); // Web API后续配置... await webApi.StartAsync(cancellationToken);
我希望能让两者共享同一个DI容器,该怎么操作?Web API用的是WebApplicationBuilder,而BackgroundService用的是DefaultBuilder(也就是IHostBuilder),有没有优雅的实现方式?
补充:我找到了相关文档,里面提到通用主机的ConfigureWebHostDefaults方法,但示例不够完整。我可以在通用主机上配置AddControllers()和AddRouting()这类服务,但调用Build()之后就没法再配置UseHttpLogging()、UseSwagger()或者MapControllers()了。
最优雅的方式是基于WebApplicationBuilder来统一构建整个应用,因为它本质上已经集成了通用主机的能力,既能托管后台服务,也能配置Web API,天然共享同一个DI容器。具体步骤如下:
- 替换原有的
Host.CreateDefaultBuilder为WebApplication.CreateBuilder,它会自动创建包含Web功能的通用主机 - 在同一个构建器中同时配置后台服务和Web API的服务
- 构建完成后,正常配置Web中间件和路由
示例代码如下:
var builder = WebApplication.CreateBuilder(args); // 1. 配置共享的DI服务(包括原Worker应用的服务) builder.Services.AddHostedService<MyBackgroundService>(); builder.Services.AddSingleton<IMyType>(myinstance); // 其他共享服务配置... // 2. 配置Web API所需的服务 builder.Services.AddControllers(); builder.Services.AddHttpLogging(); builder.Services.AddSwaggerGen(); // 其他Web服务配置... var app = builder.Build(); // 3. 配置Web中间件(现在可以正常使用UseHttpLogging、UseSwagger等) if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpLogging(); app.UseRouting(); app.UseAuthorization(); // 4. 映射API路由 app.MapControllers(); // 5. 启动应用(同时托管后台服务和Web API) await app.RunAsync();
核心优势
WebApplicationBuilder底层基于通用主机实现,完全兼容HostedService托管需求- 所有服务注册在同一DI容器,后台服务与Web API可直接注入共享实例
- 构建后的
WebApplication支持完整的中间件管道配置,解决了Build()后无法配置中间件的问题
兼容方案:基于通用主机配置
如果不想切换到WebApplicationBuilder,可以通过ConfigureWebHostDefaults扩展方法在通用主机上整合Web功能,中间件配置需嵌套在ConfigureWebHostDefaults内部:
var host = Host.CreateDefaultBuilder(args) .ConfigureServices(services => { // 配置后台服务与共享DI services.AddHostedService<MyBackgroundService>(); services.AddSingleton<IMyType>(myinstance); // 配置Web API服务 services.AddControllers(); services.AddHttpLogging(); services.AddSwaggerGen(); }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.Configure(app => { // 配置Web中间件 if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpLogging(); app.UseRouting(); app.UseAuthorization(); app.MapControllers(); }); }) .Build(); await host.RunAsync();
这种方式也能实现共享DI,但可读性不如第一种方案,建议优先选择WebApplicationBuilder的统一构建方式。
内容的提问来源于stack exchange,提问作者Mark

