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

Quartz.NET使用WithCronSchedule报错:'?'后存在非法字符|

问题分析与解决方案

首先,你遇到的Illegal character after '?': |异常,根源是Quartz.NET的CronTrigger并不支持用|符号拼接多个Cron表达式——这个写法不是Quartz认可的语法,所以直接触发了非法字符报错。

你的需求是每日00:00和12:00各执行一次任务,这里有两种靠谱的实现方式,我分别给你讲清楚:

方案1:合并成单个Cron表达式

Cron表达式原生支持用逗号分隔多个时间值,所以你可以把两个时间点合并成一个表达式,这样只需要一个触发器就能搞定:

0 0 0,12 * * ?

这个表达式的含义就是:每天的0点整和12点整执行任务。

修正后的代码(合并Cron版)

我还修复了代码里的几个异步规范问题(比如async static void改成async static Task,避免用.Result/.Wait()阻塞,改用await):

public class JobScheduler 
{ 
    public async static Task Start() 
    { 
        // 合并后的Cron表达式
        string cronExpression = "0 0 0,12 * * ?"; 
        
        IScheduler scheduler = await StdSchedulerFactory.GetDefaultScheduler(); 
        await scheduler.Start(); 
        
        IJobDetail job = JobBuilder.Create<MyJobClass>()
            .WithIdentity("MyJobKey", "MyJobGroup")
            .Build(); 
            
        ITrigger trigger = TriggerBuilder.Create() 
            .WithDescription("DailyTwiceTrigger")
            .WithIdentity("MyJobTriggerKey", "MyJobGroup") // 触发器ID和Job区分开,避免冲突
            .WithCronSchedule(cronExpression) 
            .StartAt(DateTime.UtcNow) 
            .WithPriority(1) 
            .Build(); 
            
        bool isExists = await scheduler.CheckExists(job.Key); 
        if (isExists) 
        { 
            await scheduler.RescheduleJob(new TriggerKey("MyJobTriggerKey", "MyJobGroup"), trigger); 
        } 
        else 
        { 
            await scheduler.ScheduleJob(job, trigger); 
        } 
    } 
}

方案2:创建两个独立的触发器

如果你更倾向于保留两个独立的时间配置(比如后续可能要分别调整),可以给同一个Job绑定两个CronTrigger:

修正后的代码(双触发器版)

public class JobScheduler 
{ 
    public async static Task Start() 
    { 
        string noonJobCron = "0 0 12 * * ?"; 
        string midnightJobCron = "0 0 0 * * ?"; 
        
        IScheduler scheduler = await StdSchedulerFactory.GetDefaultScheduler(); 
        await scheduler.Start(); 
        
        IJobDetail job = JobBuilder.Create<MyJobClass>()
            .WithIdentity("MyJobKey", "MyJobGroup")
            .Build(); 
            
        // 创建第一个触发器(中午12点)
        ITrigger noonTrigger = TriggerBuilder.Create() 
            .WithDescription("NoonTrigger")
            .WithIdentity("NoonTriggerKey", "MyJobGroup")
            .WithCronSchedule(noonJobCron) 
            .StartAt(DateTime.UtcNow) 
            .WithPriority(1) 
            .Build(); 
            
        // 创建第二个触发器(午夜0点)
        ITrigger midnightTrigger = TriggerBuilder.Create() 
            .WithDescription("MidnightTrigger")
            .WithIdentity("MidnightTriggerKey", "MyJobGroup")
            .WithCronSchedule(midnightJobCron) 
            .StartAt(DateTime.UtcNow) 
            .WithPriority(1) 
            .Build(); 
            
        bool jobExists = await scheduler.CheckExists(job.Key); 
        if (jobExists) 
        { 
            // 如果Job已存在,分别更新两个触发器
            await scheduler.RescheduleJob(new TriggerKey("NoonTriggerKey", "MyJobGroup"), noonTrigger);
            await scheduler.RescheduleJob(new TriggerKey("MidnightTriggerKey", "MyJobGroup"), midnightTrigger);
        } 
        else 
        { 
            // 首次调度,同时绑定Job和两个触发器
            await scheduler.ScheduleJob(job, new List<ITrigger> { noonTrigger, midnightTrigger }, true);
        } 
    } 
}

额外注意点

  1. 避免使用async static void:这种写法无法被正确捕获异常,也无法等待完成,改成async static Task是更规范的异步写法。
  2. 触发器和Job的Identity要区分开:不要给触发器和Job用完全相同的Key,否则容易出现冲突或混淆。
  3. 尽量用await替代.Result/.Wait():后者会导致线程阻塞,容易引发死锁问题,异步代码里优先使用await。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:48:34