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

如何向Job注入服务并正确配置IOC容器解决DI生命周期异常

问题根源

核心问题不是Dependency Injection生命周期配置错误,是Quartz.NET默认使用内置实例化逻辑创建Job,不会主动从ASP.NET Core的DI容器解析构造函数依赖:

  • 无参构造的Job可以被Quartz直接new出来,Execute方法正常执行
  • 带构造函数依赖注入的Job会因为Quartz找不到对应传参,实例化失败,无法触发执行
  • 全Singleton配置下Job能运行属于特殊情况:根容器可以直接解析单例实例,但这个方案完全违反EF Core DbContext的使用规范,会引发缓存脏读、并发操作冲突等问题,不能用于生产环境。
修复步骤

1. 配置Quartz使用DI容器解析Job

Quartz默认的Job工厂不会走DI容器,需要自定义实现支持DI的工厂替换默认实现。
首先在Startup.cs的ConfigureServices方法中注册自定义Job工厂和相关服务,原有服务的生命周期配置不需要修改:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddDbContext<EFDbContext>(options => options
        .UseSqlServer(Configuration.GetConnectionString("Cnn"))
        .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking), 
        ServiceLifetime.Transient);
    services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));

    // 注册自定义Job工厂,替换Quartz默认实现
    services.AddSingleton<IJobFactory, ScopedJobFactory>();
    services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();

    // 原有Job、服务注册保留,不需要改生命周期
    services.AddTransient<CheckTransactionConfirmJob>();
    services.AddTransient<CheckPaymentConfirmationScheduler>();
    services.AddTransient<IPaymentRepository, EfPlanRepository>();
}

添加自定义Job工厂实现,负责从DI容器解析Job实例:

public class ScopedJobFactory : IJobFactory
{
    private readonly IServiceProvider _rootServiceProvider;
    public ScopedJobFactory(IServiceProvider rootServiceProvider)
    {
        _rootServiceProvider = rootServiceProvider;
    }

    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
        var jobType = bundle.JobDetail.JobType;
        return (IJob)_rootServiceProvider.GetRequiredService(jobType);
    }

    public void ReturnJob(IJob job)
    {
        if (job is IDisposable disposable)
        {
            disposable.Dispose();
        }
    }
}

在调度器初始化逻辑中,给Scheduler实例指定自定义的Job工厂(在CheckPaymentConfirmationScheduler初始化Scheduler的位置添加代码即可):

var scheduler = await schedulerFactory.GetScheduler();
// 赋值DI Job工厂
scheduler.JobFactory = _rootServiceProvider.GetRequiredService<IJobFactory>();

2. 修正Job内的依赖解析方式

Quartz执行Job时没有ASP.NET Core请求管道的天然服务Scope,直接在Job构造函数注入Transient/Scoped生命周期的服务(比如DbContext、依赖DbContext的Repository),会导致服务从根容器解析、长期不释放,完全违背Transient生命周期的设计预期。
正确的做法是仅在Job构造函数注入根IServiceProvider,每次执行Execute方法时手动创建独立服务Scope,在Scope内解析需要的服务,执行完成后Scope自动释放所有相关服务(包括DbContext)。
修改后的CheckTransactionConfirmJob代码如下:

public class CheckTransactionConfirmJob : IJob
{
    private readonly IServiceProvider _rootServiceProvider;

    // 仅注入根服务提供者,不要直接注入Repository、DbContext这类短生命周期服务
    public CheckTransactionConfirmJob(IServiceProvider rootServiceProvider)
    {
        _rootServiceProvider = rootServiceProvider;
    }

    public async Task Execute(IJobExecutionContext context)
    {
        try
        {
            // 每次执行创建独立服务域,执行完自动释放所有域内服务
            using var scope = _rootServiceProvider.CreateScope();
            var paymentRepository = scope.ServiceProvider.GetRequiredService<IPaymentRepository>();
            
            // 从JobDataMap读取传入参数
            var paymentId = context.JobDetail.JobDataMap.GetGuid("paymentId");
            var userId = context.JobDetail.JobDataMap.GetGuid("userId");

            // 执行业务逻辑
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }
}
注意事项
  • 原有RunPaymentCheckerJob方法中创建Job、Trigger的逻辑不需要修改,Quartz会自动通过配置的DI工厂解析Job实例
  • 不需要修改DbContext的Transient生命周期配置,符合EF Core的最佳实践
  • 所有短生命周期服务(DbContext、Repository、业务服务等)都要在Execute方法的Scope内解析,不要直接注入Job构造函数,避免服务长期复用引发问题

内容的提问来源于stack exchange,提问作者Sasan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 12:19:51