Quartz.NET:如何通过.NET依赖注入为带非空构造函数的作业注入依赖
看起来你遇到的核心问题是调度器没有正确关联你配置的Microsoft DI作业工厂,导致它仍在尝试用默认逻辑(无参构造函数)实例化你的作业。结合Quartz 3.2.4的特性,我来一步步帮你修正:
1. 修正核心配置逻辑
你的Quartz配置已经接近正确,但有几个关键细节需要调整:
正确的DI注册步骤
在Program.cs(或Startup.cs)中,按以下方式配置Quartz服务:
// 注册Quartz并配置DI作业工厂 services.AddQuartz(q => { // 启用Microsoft DI作用域作业工厂(这是让作业从DI容器获取实例的关键) q.UseMicrosoftDependencyInjectionScopedJobFactory(); // 注册作业到Quartz系统(同时会自动关联DI容器) var jobKey = new JobKey("MyID", "MyGroup"); q.AddJob<MyJob>(opts => opts.WithIdentity(jobKey)); // 配置触发器并绑定到目标作业 q.AddTrigger(opts => opts .ForJob(jobKey) .WithIdentity("MyTriggerID") // 建议触发器ID与作业区分开,避免冲突 .StartNow() .WithCronSchedule("*/1 * * * * ?")); // 每秒执行一次 }); // 添加Quartz托管服务,让调度器在后台自动启动并管理生命周期 services.AddQuartzHostedService(options => { // 应用关闭时等待正在执行的作业完成 options.WaitForJobsToComplete = true; }); // 显式注册作业到DI容器(可选,但确保Scoped生命周期的作业能被正确解析) services.AddScoped<MyJob>();
为什么之前的配置失效?
你之前手动创建JobDetail和Trigger并调用_scheduler.ScheduleJob,但如果调度器实例不是通过DI容器获取的(而是手动构建的),它不会自动关联你配置的MicrosoftDependencyInjectionScopedJobFactory。使用AddQuartzHostedService会让DI自动管理调度器的创建与配置,确保作业工厂被正确应用。
2. 修正作业类的小问题
你的Execute方法缺少返回值,会导致编译错误,需要补充返回Task.CompletedTask:
public class MyJob : IJob { private readonly ILogger<MyJob> _logger; public MyJob(ILogger<MyJob> logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public Task Execute(IJobExecutionContext context) { _logger.LogDebug("It's working!"); return Task.CompletedTask; // 必须返回已完成的Task } }
3. 验证包版本一致性
确保你安装的Quartz和Quartz.Extensions.DependencyInjection版本完全一致(都是3.2.4),版本不匹配很容易导致DI集成失效。你可以在NuGet包管理器中检查,或者查看.csproj文件:
<PackageReference Include="Quartz" Version="3.2.4" /> <PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.2.4" />
4. 避免手动创建调度器(重要)
如果你之前是自己手动实例化IScheduler,请停止这种做法!通过AddQuartzHostedService,DI会自动为你创建并初始化调度器,且自动应用配置的作业工厂。
如果确实需要手动操作调度器,应通过DI注入ISchedulerFactory来获取实例:
public class SomeService { private readonly ISchedulerFactory _schedulerFactory; public SomeService(ISchedulerFactory schedulerFactory) { _schedulerFactory = schedulerFactory; } public async Task ScheduleJobManually() { var scheduler = await _schedulerFactory.GetScheduler(); // 此处的调度器已绑定配置的DI作业工厂 // 可继续添加作业/触发器,但推荐优先在AddQuartz中配置 } }
按照以上步骤调整后,你的作业就能正确从DI容器获取ILogger<MyJob>实例,不再抛出无参构造函数的错误。
内容的提问来源于stack exchange,提问作者Wouter Vandenputte




