ASP.NET Core启动时数据库异常如何终止Hangfire Server
解决Hangfire在数据库连接失败后持续重试的问题
我有一个基于ASP.NET Core的项目,使用Hangfire结合SQL Server存储处理定时任务。项目启动时会先检查数据库连接是否有效,如果数据库有问题,中间件会显示错误信息并终止应用。简化后的Program.cs代码如下:
WebApplicationBuilder builder = WebApplication.CreateBuilder(args); // (Register other services) builder.Services.AddHangfire(config => { config.SetDataCompatibilityLevel(CompatibilityLevel.Version_180); config.UseSimpleAssemblyNameTypeSerializer(); config.UseRecommendedSerializerSettings(); config.UseSqlServerStorage("connectionString"); }); WebApplication app = builder.Build(); using IServiceScope scope = app.Services.CreateScope(); IServiceProvider services = scope.ServiceProvider; IDatabaseService database = services.GetRequiredService<IDatabaseService>(); // 数据库异常时设置标记 await database.EnsureInitializedAsync(); // 检查标记并返回错误响应,终止请求和进程 app.UseMiddleware<DatabaseInitializationMiddleware>(); app.MapDefaultControllerRoute(); app.Run();
添加Hangfire后,当数据库连接无效时,即便已经通过EnsureInitializedAsync()检测到问题,应用仍会每15秒重复抛出SqlException。我希望在数据库检测出问题后停止Hangfire,避免它持续尝试连接直到应用终止。
我曾尝试在数据库初始化检查后添加以下代码:
using BackgroundJobServer server = new BackgroundJobServer(JobStorage.Current); server.SendStop(); await server.WaitForShutdownAsync(default);
但执行时出现错误:
System.InvalidOperationException: 'Current JobStorage instance has not been initialized yet.
问题原因
错误源于JobStorage.Current尚未完成初始化,Hangfire的配置和服务都是通过DI容器注册的,直接实例化BackgroundJobServer无法获取正确的配置上下文。
解决方案
方案一:按需注册Hangfire服务(推荐)
提前检查数据库状态,仅当数据库连接正常时才注册Hangfire相关服务,从根源避免无效重试:
WebApplicationBuilder builder = WebApplication.CreateBuilder(args); // 构建临时服务容器检查数据库状态 using (var tempScope = builder.Services.BuildServiceProvider().CreateScope()) { var database = tempScope.ServiceProvider.GetRequiredService<IDatabaseService>(); bool isDatabaseHealthy = await database.EnsureInitializedAsync(); if (isDatabaseHealthy) { // 数据库正常才注册Hangfire builder.Services.AddHangfire(config => { config.SetDataCompatibilityLevel(CompatibilityLevel.Version_180); config.UseSimpleAssemblyNameTypeSerializer(); config.UseRecommendedSerializerSettings(); config.UseSqlServerStorage("connectionString"); }); builder.Services.AddHangfireServer(); // 注册Hangfire服务端 } } WebApplication app = builder.Build(); app.UseMiddleware<DatabaseInitializationMiddleware>(); app.MapDefaultControllerRoute(); app.Run();
方案二:数据库异常时主动停止Hangfire服务
如果必须提前注册Hangfire服务,可在数据库检查失败后从DI容器获取服务端实例并停止:
WebApplicationBuilder builder = WebApplication.CreateBuilder(args); // 正常注册Hangfire服务 builder.Services.AddHangfire(config => { config.SetDataCompatibilityLevel(CompatibilityLevel.Version_180); config.UseSimpleAssemblyNameTypeSerializer(); config.UseRecommendedSerializerSettings(); config.UseSqlServerStorage("connectionString"); }); builder.Services.AddHangfireServer(); WebApplication app = builder.Build(); using IServiceScope scope = app.Services.CreateScope(); IServiceProvider services = scope.ServiceProvider; IDatabaseService database = services.GetRequiredService<IDatabaseService>(); bool isDatabaseHealthy = await database.EnsureInitializedAsync(); if (!isDatabaseHealthy) { // 获取Hangfire服务端并停止 var hangfireServer = services.GetService<IBackgroundJobServer>(); if (hangfireServer != null) { hangfireServer.SendStop(); await hangfireServer.WaitForShutdownAsync(default); } } app.UseMiddleware<DatabaseInitializationMiddleware>(); app.MapDefaultControllerRoute(); app.Run();
说明
- 方案一效率更高,数据库异常时不会初始化任何Hangfire资源,彻底避免了后续的重试逻辑。
- 方案二适用于需要提前注册Hangfire服务的场景,通过主动停止服务端终止Hangfire的后台连接尝试。
内容的提问来源于stack exchange,提问作者Valuator
相关产品推荐
相关产品推荐

