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

Blazor Server端订阅Quartz监听器事件未触发问题求助

解决Blazor中Quartz.NET JobListener事件订阅不触发的问题

嘿,我来帮你排查这个问题——你遇到的情况大概率是监听器实例不共享,再加上异步方法的写法有问题,我们一步步拆解解决:

1. 最核心的问题:Quartz和Blazor用的不是同一个监听器实例

你在ConfigureServices里把GlobalJobListener注册成了Scoped服务,但如果Quartz注册监听器的时候是直接new了一个新实例(比如scheduler.ListenerManager.AddJobListener(new GlobalJobListener("Global"));),那麻烦就来了:Blazor页面注入的是DI容器里的Scoped实例,而Quartz用的是它自己创建的另一个实例。你在Blazor里订阅的是前者的事件,但Quartz触发的是后者的事件,自然不会调用你的BeforeStart方法。

怎么修复?

确保Quartz使用的监听器来自DI容器:

  • 如果你是手动配置Quartz,从DI获取实例再添加:
services.AddScoped<GlobalJobListener>();

services.AddQuartz(q =>
{
    // 先完成其他Quartz配置(比如添加Job、Trigger)
    
    // 从DI容器获取监听器实例
    using var scope = services.BuildServiceProvider().CreateScope();
    var listener = scope.ServiceProvider.GetRequiredService<GlobalJobListener>();
    q.Listeners.Add(listener);
});
  • 更优雅的方式是让Quartz自动从DI解析监听器:
services.AddQuartz(q =>
{
    // 添加监听器,Quartz会自动从DI获取实例
    q.AddJobListener<GlobalJobListener>(listenerName: "GlobalJobListener");
});

// 别忘了添加Quartz的HostedService
services.AddQuartzHostedService(options =>
{
    options.WaitForJobsToComplete = true;
});

2. 异步方法的写法完全错了

你的JobToBeExecuted里手动创建Task、调用Start()再Wait(),这完全是冗余操作,还容易导致线程阻塞和上下文同步问题。

修正后的监听器方法

假设你的事件委托是同步的(比如public delegate void TaskExecution();),直接调用事件就行:

public Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default)
{
    // 直接触发事件,返回已完成的Task
    Started?.Invoke();
    return Task.CompletedTask;
}

public Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default)
{
    Vetoed?.Invoke();
    return Task.CompletedTask;
}

public Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default)
{
    Executed?.Invoke(jobException);
    return Task.CompletedTask;
}

如果你的事件是异步委托(比如public delegate Task TaskExecution();),就用await调用:

public async Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default)
{
    if (Started != null)
    {
        await Started.Invoke();
    }
}

3. 验证Blazor的订阅逻辑是否执行

你可以在OnInitializedAsync里加个日志,确认订阅逻辑真的跑了:

protected override async Task OnInitializedAsync()
{
    // 注意这里如果scheduler是异步获取的,要加await
    scheduler = await quartz.Scheduler; 
    if (scheduler != null)
    {
        Log.Information("已订阅JobListener的Started和Executed事件");
        listener.Started += BeforeStart;
        listener.Executed += AfterEndAsync;
    }
    else
    {
        Log.Warning("Scheduler为null,没订阅到事件");
    }
}

4. 检查事件委托的定义

确保你的TaskExecution和TaskExecutionComplete委托和调用时的参数匹配,比如:

// 同步委托
public delegate void TaskExecution();
public delegate void TaskExecutionComplete(JobExecutionException exception);

// 异步委托(如果需要异步处理事件)
public delegate Task TaskExecution();
public delegate Task TaskExecutionComplete(JobExecutionException exception);

要是委托定义和调用参数不匹配,编译器可能没报错,但运行时事件就是触发不了。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 18:22:54