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

.NET 8中Quartz.NET非生产环境一次性作业触发失败问题

问题描述

使用.NET 8搭建Quartz Job时,需求如下:

  • 生产环境通过Cron表达式调度作业
  • 非生产(开发、本地等)环境仅注册作业不调度,通过Controller端点触发一次性作业

已配置的代码如下:

Startup.cs 配置

var environmentType = Environment.Type;
services.AddQuartz();
services.AddQuartzHostedService(opt =>
{
    opt.WaitForJobsToComplete = true;
});

if (Environment.Type == EnvironmentType.Production)
{
    services.ConfigureOptions<ProcessIssuersJobSetupWithCron>();
}
else if (environmentType is EnvironmentType.TestingRc or EnvironmentType.TestingMaster or EnvironmentType.Staging or EnvironmentType.Local)
{
    services.ConfigureOptions<ProcessIssuersJobSetupWithoutCron>();
}

非生产环境作业配置类 ProcessIssuersJobSetupWithoutCron

public class ProcessIssuersJobSetupWithoutCron : IConfigureOptions<QuartzOptions>
{
    public void Configure(QuartzOptions options)
    {
        var processIssuersJobKey = JobKey.Create("ProcessIssuersJob");

        options
            .AddJob<ProcessIssuersJob>(jobBuilder => jobBuilder.WithIdentity(processIssuersJobKey))
            .AddTrigger(trigger =>
                trigger
                    .ForJob(jobKey: processIssuersJobKey));
    }
}

Controller触发代码

public class QuartzController(ISchedulerFactory scheduleFactory) : Controller
{
    private readonly ISchedulerFactory _scheduleFactory = scheduleFactory;

    [HttpPost("ProcessIssuers")]
    public async Task<IActionResult> ExecuteDataStoredProcedures([FromQuery] DateTime runDate)
    {
        var scheduler = await this._scheduleFactory.GetScheduler();
        var jobKey = JobKey.Create("ProcessIssuersJob");

        var jobDataMap = new JobDataMap();
        jobDataMap.Put("runDate", runDate);

        await scheduler.TriggerJob(jobKey, jobDataMap);

        return this.Ok();
    }
}

执行await scheduler.TriggerJob(jobKey, jobDataMap)时出现错误:

Exception Type: Quartz.JobPersistenceException
Error message: The job (DEFAULT.ProcessIssuersJob) referenced by the trigger does not exist.

调试确认ProcessIssuersJobSetupWithoutCron的Configure方法已执行,且JobKey与触发时一致为DEFAULT.ProcessIssuersJob,生产环境的Cron配置下一次性作业可正常触发,问题出在哪里?

问题原因及解决办法

核心原因

在非生产环境的配置中,你只通过AddTrigger关联了触发器和作业,但没有给触发器设置任何触发规则,导致Quartz不会将该作业持久化到调度器的作业存储中。而生产环境的Cron配置中,触发器有明确的Cron表达式,Quartz会自动完成作业的注册与绑定。

解决步骤

方案1:移除无效的触发器配置,仅注册作业

修改ProcessIssuersJobSetupWithoutCron,只添加作业,不添加无触发规则的触发器:

public class ProcessIssuersJobSetupWithoutCron : IConfigureOptions<QuartzOptions>
{
    public void Configure(QuartzOptions options)
    {
        var processIssuersJobKey = JobKey.Create("ProcessIssuersJob");

        // 仅注册作业,不添加触发器
        options.AddJob<ProcessIssuersJob>(jobBuilder => jobBuilder.WithIdentity(processIssuersJobKey));
    }
}

方案2:给触发器设置一个永远不会触发的规则(保留触发器结构)

如果需要保留触发器的配置结构,可以给触发器设置一个过去的时间作为触发时间,确保它不会自动执行,但能让Quartz完成作业的注册:

public class ProcessIssuersJobSetupWithoutCron : IConfigureOptions<QuartzOptions>
{
    public void Configure(QuartzOptions options)
    {
        var processIssuersJobKey = JobKey.Create("ProcessIssuersJob");

        options
            .AddJob<ProcessIssuersJob>(jobBuilder => jobBuilder.WithIdentity(processIssuersJobKey))
            .AddTrigger(trigger =>
                trigger
                    .ForJob(jobKey: processIssuersJobKey)
                    // 设置一个过去的时间,确保不会自动触发
                    .StartAt(DateTimeOffset.UtcNow.AddDays(-1)));
    }
}

验证逻辑

当你调用TriggerJob时,Quartz需要确认作业已经在调度器中存在。如果只添加了无触发规则的触发器,Quartz不会将作业正式注册到调度器的作业存储中,导致触发时提示作业不存在。移除无效触发器或给触发器设置有效(但不自动执行)的规则后,作业会被正确注册,就能通过TriggerJob正常触发。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 00:30:06