Quartz.Net数据库作业仅单例添加可用,动态添加触发空引用异常求助
问题描述
我开发了一个将作业存入数据库并按需调度的程序。此前在Program.cs中以单例方式添加作业可正常运行,但每个调度器仅能添加一个作业,且无法检查作业是否已存在。因此我将作业添加逻辑移至调度器中,先检查数据库中是否存在目标作业,不存在则添加。
作业可成功添加,但完全无法运行,调用时会触发来自JobRunShell.Run()的NullReferenceException。作业在数据库中的状态看起来完全正常,代码也未发现错误。这种添加作业的方式是否不正确?
以单例添加作业的Program.cs代码
builder.Services.AddSingleton<IJobFactory, JobFactory>(); builder.Services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>(); builder.Services.AddSingleton<MailHRNewEmployee>(); builder.Services.AddSingleton(new JobMetadata(Guid.NewGuid(), typeof(MailHRNewEmployee), "HR First Contact", "job blueprint")); builder.Services.AddHostedService<MyScheduler>();
MyScheduler.cs代码
public async Task StartAsync(CancellationToken cancellationToken) { //Creating Scheduler Scheduler = await schedulerFactory.GetScheduler(); Scheduler.JobFactory = jobFactory; //Start Scheduler await Scheduler.Start(cancellationToken); //Create Jobs var istrue = await Scheduler.CheckExists(new JobKey("HR First Contact", "DEFAULT")); if (!(bool)istrue) { jobMetaData = new JobMetadata(Guid.NewGuid(), typeof(MailHRNewEmployee), "HR First Contact", "job blueprint"); IJobDetail jobDetail = CreateJob(jobMetaData); await Scheduler.AddJob(jobDetail, true); } ...(checking all the other jobs) private IJobDetail CreateJob(JobMetadata jobMetadata) { return JobBuilder.Create(jobMetaData.JobType).WithIdentity(jobMetaData.JobName.ToString()).StoreDurably(true).WithDescription(jobMetaData.JobDescription).Build(); }
NullReferenceException异常栈
[10:43:31 ERR] Job DEFAULT.HR First Contact threw an unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object. at Quartz.Core.JobRunShell.Run(CancellationToken cancellationToken) [10:43:32 ERR] Job DEFAULT.HR First Contact threw an exception. Quartz.SchedulerException: Job threw an unhandled exception. ---> System.NullReferenceException: Object reference not set to an instance of an object. at Quartz.Core.JobRunShell.Run(CancellationToken cancellationToken) --- End of inner exception stack trace --- [See nested exception: System.NullReferenceException: Object reference not set to an instance of an object. at Quartz.Core.JobRunShell.Run(CancellationToken cancellationToken)]
补充说明:
我尝试使用MS DI集成库配合Quartz托管服务,但无法使其与持久化作业存储及MySQL配合工作。
builder.Services.Configure<QuartzOptions>(builder.Configuration.GetSection("Quartz")); builder.Services.Configure<QuartzOptions>(options => { options.Scheduling.IgnoreDuplicates = true; options.Scheduling.OverWriteExistingData = true; }); builder.Services.AddQuartz(q => { q.SchedulerId = "Job Creator"; q.UseMicrosoftDependencyInjectionJobFactory(); var jobKey = new JobKey("HR First Contact", "DEFAULT"); q.AddJob<MailHRNewEmployee>(jobKey, j => j .StoreDurably() .WithDescription("job blueprint")); q.UsePersistentStore(s => { s.PerformSchemaValidation = true; s.UseProperties = true; s.RetryInterval = TimeSpan.FromSeconds(15); //Neither .UseMySql nor UseMySqlConnector work //MySqlConnector should be the one I want though s.UseMySqlConnector(MySql => { //I am loading the configuration above but don't know how to use the connection string here? //I thought it would maybe be Configuration.GetConnectionString("xyz"); MySql.ConnectionString = "MyConnectionString"; MySql.TablePrefix = "QRTZ_"; }); s.UseJsonSerializer(); }); }); builder.Services.AddTransient<MailHRNewEmployee>(); builder.Services.AddQuartzHostedService(options => { options.WaitForJobsToComplete = true; });
解决方案
一、调度器内添加作业导致空引用的修复
1. 修正CreateJob方法的参数错误
你的CreateJob方法参数是jobMetadata,但方法内误用了类成员jobMetaData(大小写不一致),如果类成员未初始化,会导致JobDetail关联错误的类型信息,直接引发空引用。修复代码:
private IJobDetail CreateJob(JobMetadata jobMetadata) { return JobBuilder.Create(jobMetadata.JobType) .WithIdentity(jobMetadata.JobName) .StoreDurably(true) .WithDescription(jobMetadata.JobDescription) .Build(); }
2. 确保作业实例能被DI容器正确解析
动态添加作业时,必须保证作业类型(如MailHRNewEmployee)已注册到DI容器,且自定义JobFactory能正确从容器获取实例:
- 注册作业:使用
AddTransient或AddScoped(单例仅适用于无状态作业):builder.Services.AddTransient<MailHRNewEmployee>(); - 检查自定义
JobFactory实现,确保正确解析实例:public class JobFactory : IJobFactory { private readonly IServiceProvider _serviceProvider; public JobFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { return _serviceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob; } public void ReturnJob(IJob job) { if (job is IDisposable disposable) { disposable.Dispose(); } } }
二、MS DI集成库配合MySQL持久化的配置修复
1. 安装正确的NuGet包
确保已安装:
Quartz.Extensions.DependencyInjectionQuartz.Persistence.MySqlConnector(使用官方MySqlConnector驱动,而非旧版MySql.Data)
2. 正确读取连接字符串
通过配置文件读取连接字符串,避免硬编码:
q.UsePersistentStore(s => { s.PerformSchemaValidation = true; s.UseProperties = true; s.RetryInterval = TimeSpan.FromSeconds(15); s.UseMySqlConnector(MySql => { MySql.ConnectionString = builder.Configuration.GetConnectionString("QuartzMySQL"); MySql.TablePrefix = "QRTZ_"; MySql.MySqlVersion = new Version(8, 0, 28); // 指定MySQL版本避免兼容性问题 }); s.UseJsonSerializer(); });
在appsettings.json中添加连接字符串配置:
"ConnectionStrings": { "QuartzMySQL": "server=localhost;database=quartz;user=root;password=你的密码;SslMode=None;" }
3. 动态添加作业的适配
如果需要动态检查并添加作业,不要在AddQuartz中硬编码作业注册,而是通过IScheduler在托管服务中动态处理,避免重复注册冲突。
内容的提问来源于stack exchange,提问作者Jat
相关产品推荐
相关产品推荐

