.NET 6控制台Hangfire定时任务依赖注入报错求助
问题分析与修复方案
问题根源
- 作业注册方式错误:使用闭包
() => _adminService.PruneOtpBackups()时,Hangfire会尝试序列化该闭包中的_adminService实例,失败后会直接尝试创建AdminService类型,但该类型无无参构造函数,导致异常。 - 自定义JobActivator未生效:Executor中手动创建了
BackgroundJobServer,未应用之前配置的自定义Activator;同时原Activator的Scope管理错误,导致服务实例提前被释放。 - Host生命周期破坏:在
ConfigureServices阶段提前构建服务提供者并执行Execute,违背了.NET Host的生命周期管理逻辑。
具体修复步骤
1. 修改作业注册方式,依赖Activator解析服务
将Executor中的定时任务注册改为直接指定服务类型的方式,让Hangfire通过自定义Activator解析IAdminService:
public void Execute() { try { // 直接指定服务类型,让Hangfire通过Activator获取实例 RecurringJob.AddOrUpdate<IAdminService>( adminService => adminService.PruneOtpBackups(), "*/2 * * * *"); Console.WriteLine("Hangfire Server started. Press any key to exit..."); Console.ReadKey(); } catch (Exception ex) { Log.Error(ex, "An error occurred!"); throw; } finally { Log.CloseAndFlush(); } }
2. 修正自定义JobActivator的Scope管理
原Activator中使用using var scope会导致Scope在方法结束后立即释放,服务实例被提前回收。重写BeginScope方法,让Scope与Job生命周期绑定:
using Hangfire; using Microsoft.Extensions.DependencyInjection; namespace Infrabel.Account.JobScheduler { public class HangfireActivator : JobActivator { private readonly IServiceScopeFactory _scopeFactory; public HangfireActivator(IServiceScopeFactory scopeFactory) { _scopeFactory = scopeFactory; } public override JobActivatorScope BeginScope(JobActivatorContext context) { var scope = _scopeFactory.CreateScope(); return new HangfireScope(scope); } private class HangfireScope : JobActivatorScope { private readonly IServiceScope _scope; public HangfireScope(IServiceScope scope) { _scope = scope; } public override object Resolve(Type type) { return _scope.ServiceProvider.GetRequiredService(type); } public override void DisposeScope() { _scope.Dispose(); } } } }
3. 移除手动创建的BackgroundJobServer,依赖Host托管
AddHangfireServer()已经将Hangfire Server注册为Host托管服务,会自动启动,无需手动创建。删除Executor中using (var server = new BackgroundJobServer())代码块。
4. 修正Program.cs的Host配置逻辑
不在ConfigureServices中提前构建服务提供者,改为Host启动后获取Executor实例执行:
using IHost host = Host.CreateDefaultBuilder(args) .ConfigureServices((context, services) => { var connString = "ConnString"; // 建议从配置文件读取,例如context.Configuration.GetConnectionString("Hangfire") services.AddAccountLib(connString); services.AddHangfire((provider, config) => { config.UseSqlServerStorage(connString) .UseActivator(new HangfireActivator(provider.GetRequiredService<IServiceScopeFactory>())); }); services.AddHangfireServer(); // 注册Hangfire托管服务 services.AddSingleton<Executor>(); }) .Build(); // Host启动后获取Executor执行 var executor = host.Services.GetRequiredService<Executor>(); executor.Execute(); await host.RunAsync();
额外验证点
- 确认
AddAccountLib方法已正确注册IAdminService与AdminService的映射关系,且生命周期配置合理(如Scoped/Singleton)。 - 确保Hangfire数据库连接字符串正确,可通过
Hangfire.SqlServer.SqlServerStorage.CreateTablesIfNotExists()手动初始化数据库表。
内容的提问来源于stack exchange,提问作者Robin
相关产品推荐
相关产品推荐

