如何正确将SignalR的IHubContext注入到Quartz.NET的Job中?
问题根因
你的控制器可正常解析IHubContext说明SignalR服务注册逻辑没有问题,报错核心出自两个问题:
- 自定义JobFactory直接从根IServiceProvider解析服务,没有为每次Job执行创建独立的服务作用域,不符合ASP.NET Core DI的生命周期管理规范,导致IHubContext的解析链路断裂
- 手动实现的JobFactory没有适配ASP.NET Core环境下的服务解析规则,遗漏了作用域的创建和释放逻辑
修复方案
方案1:使用官方Quartz.AspNetCore集成包(推荐)
不需要手动实现JobFactory,官方封装的组件已经原生支持DI注入,适配ASP.NET Core的服务管理规则:
- 先通过Nuget安装
Quartz.AspNetCore包 - 调整Program.cs/Startup.cs的服务注册逻辑:
// 你已有的SignalR注册逻辑,无需修改 builder.Services.AddSignalR(); // 注册Quartz服务 builder.Services.AddQuartz(q => { // 注册MyJob,自动支持DI注入 q.AddJob<MyJob>(j => j.WithIdentity(nameof(MyJob))); // 配置触发器,可根据业务需求调整执行规则 q.AddTrigger(t => t .ForJob(nameof(MyJob)) .WithIdentity($"{nameof(MyJob)}-trigger") .WithCronSchedule("0/5 * * * * ?") // 示例:每5秒执行一次 ); }); // 注册Quartz托管服务,程序启动时自动启动调度器、停止时自动等待任务完成 builder.Services.AddQuartzHostedService(opt => { opt.WaitForJobsToComplete = true; });
- 删除你自定义的JobFactory实现即可,无需额外配置,MyJob即可正常注入IHubContext。
方案2:保留自定义JobFactory的修改方案
如果因业务要求必须保留自定义JobFactory,修改代码增加作用域管理逻辑:
public class JobFactory : IJobFactory { protected readonly IServiceProvider ServiceProvider; public JobFactory(IServiceProvider serviceProvider) => ServiceProvider = serviceProvider; public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { // 为每个Job执行创建独立的服务作用域 var scope = ServiceProvider.CreateScope(); // 将作用域存入调度器上下文,后续释放使用 scheduler.Context.Put($"job-scope-{bundle.JobDetail.Key}", scope); return scope.ServiceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob; } public void ReturnJob(IJob job) { // 此处注意要先从调度器上下文取出当前Job对应的作用域释放 (job as IDisposable)?.Dispose(); } }
修改后在Quartz配置中指定使用该自定义JobFactory即可。
验证
修改完成后即可在MyJob的Execute方法中直接调用_context.Clients.All.SendAsync()等方法向SignalR客户端推送消息。
内容的提问来源于stack exchange,提问作者teklot
相关产品推荐
相关产品推荐

