You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为BackgroundService启动的Hangfire Job注入依赖?

问题根源

你的MyCustomJob是单例生命周期的HostedService,直接注入Scoped的IFooService会导致该服务被强制提升为单例。而DbContext设计为Scoped生命周期,单例使用会引发上下文复用、线程安全等问题;同时直接捕获单例_fooService注册RecurringJob,会让Hangfire复用该实例,导致Job执行时无法获取正确的作用域,最终无法正常获取DbContext实例。


正确实现方案

步骤1:修正基础配置

  1. 补充Hangfire Server注册(必须添加才能执行Job)
  2. 修正代码笔误: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 10:05:16