如何为BackgroundService启动的Hangfire Job注入依赖?
问题根源
你的MyCustomJob是单例生命周期的HostedService,直接注入Scoped的IFooService会导致该服务被强制提升为单例。而DbContext设计为Scoped生命周期,单例使用会引发上下文复用、线程安全等问题;同时直接捕获单例_fooService注册RecurringJob,会让Hangfire复用该实例,导致Job执行时无法获取正确的作用域,最终无法正常获取DbContext实例。
正确实现方案
步骤1:修正基础配置
- 补充Hangfire Server注册(必须添加才能执行Job)
- 修正代码笔误:
builder.Service.AddScoped改为builder.Services.AddScoped
修改后的Program.cs配置:
// 注册Hangfire核心服务 builder.Services.AddHangfire(configuration => configuration .SetDataCompatibilityLevel(CompatibilityLevel.Version_170) .UseSimpleAssemblyNameTypeSerializer() .UseRecommendedSerializerSettings() .UseSqlServerStorage(builder.Configuration.GetConnectionString("DbConnection"))); // 注册Hangfire执行Server(关键) builder.Services.AddHangfireServer(); // 注册DbContext builder.Services.AddDbContext<MyCustomDbContext>(o => o.UseSqlServer( builder.Configuration.GetConnectionString("DbConnection"), options => options.EnableRetryOnFailure())); // 注册Scoped业务服务 builder.Services.AddScoped<IFooService, FooService>(); // 移除自定义HostedService注册,改用Hangfire原生调度 // builder.Services.AddHostedService<MyCustomJob>();
步骤2:改用Hangfire原生方式注册定时Job
不需要自定义IHostedService管理Job,直接在Program.cs中完成注册,利用Hangfire自动管理作用域:
var app = builder.Build(); // 其他中间件配置(如路由、授权等) // ... // 可选:注册Hangfire Dashboard,用于查看Job状态 app.UseHangfireDashboard(); // 注册RecurringJob,Hangfire会自动处理作用域 using (var scope = app.Services.CreateScope()) { var recurringJobManager = scope.ServiceProvider.GetRequiredService<IRecurringJobManager>(); // 泛型方式注册,每次执行时Hangfire会创建新作用域获取Scoped服务 recurringJobManager.AddOrUpdate<IFooService>( "Do Something", service => service.DoSomething(), Cron.Minutely); } app.Run();
步骤3:修正FooService构造函数错误
修复参数类型错误,移除多余的无效代码:
public class FooService: IFooService { private readonly ILogger<FooService> _logger; private readonly INugetService _nugetService; // 修正参数类型错误(InugetService → INugetService) public FooService(ILogger<FooService> logger, INugetService nugetService) { _logger = logger; _nugetService = nugetService; // 移除无意义的_serviceProvider赋值(原代码无对应参数) } public async Task DoSomething() { _logger.LogInformation($"Doing Something at {DateTime.UtcNow} time"); var result = await _nugetService.Do(); // 补充await保证异步执行 // 业务逻辑处理 if (/* 错误判断条件 */) { throw new Exception("自定义错误提示"); // 实例化Exception,不能直接throw Exception类型 } } }
核心原理说明
Hangfire的泛型AddOrUpdate<T>注册方式会自动处理依赖注入:每次Job执行时,Hangfire会创建一个新的作用域,从作用域中获取Scoped生命周期的IFooService实例,从而保证DbContext也处于正确的Scoped生命周期,彻底解决单例复用导致的上下文获取问题。
内容的提问来源于stack exchange,提问作者Jaspreet Singh
相关产品推荐
相关产品推荐

