What it is
Quartz.NET is a full-featured job scheduler for .NET with cron triggers, persistent job stores, clustering and misfire handling.
Jobs implement IJob; triggers describe when they fire. With a persistent store, schedules survive restarts and can be coordinated across instances.
Installation
dotnet add package Quartz.Extensions.HostingGetting started
The smallest useful thing you can do with it, and what each part means.
csharp
public class ReportJob : IJob
{
private readonly IReportService _reports;
public ReportJob(IReportService reports) => _reports = reports;
public async Task Execute(IJobExecutionContext context)
{
var date = context.MergedJobDataMap.GetString("date");
await _reports.GenerateAsync(date, context.CancellationToken);
}
}
builder.Services.AddQuartz(q =>
{
var key = new JobKey("nightly-report");
q.AddJob<ReportJob>(o => o.WithIdentity(key));
q.AddTrigger(o => o
.ForJob(key)
.WithIdentity("nightly-trigger")
.WithCronSchedule("0 0 3 * * ?", x => x.InTimeZone(TimeZoneInfo.Utc)));
});
builder.Services.AddQuartzHostedService(o => o.WaitForJobsToComplete = true);Advanced usage
Where the library earns its place over a simpler alternative.
csharp
q.UsePersistentStore(s =>
{
s.UseProperties = true;
s.UseSqlServer(connectionString);
s.UseClustering(); // exactly one instance runs each trigger
});
// What to do when the scheduler was down at fire time.
.WithCronSchedule("0 0 3 * * ?", x => x
.WithMisfireHandlingInstructionDoNothing()) // skip the missed run
// Prevent overlapping executions of the same job.
[DisallowConcurrentExecution]
public class ReportJob : IJob { }Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- CronExpression is invalid
- Almost always a five-field Unix expression. Quartz needs seconds first and a ? in either day-of-month or day-of-week.
- Jobs run on every instance
- Clustering is not enabled, or instances share a store but not the clustering setting.
Best practices
- Remember Quartz cron includes a seconds field; validate expressions before deploying.
- Use UseClustering with a persistent store for multi-instance deployments.
- Set an explicit time zone; relying on the server's local zone breaks across DST.
- Add [DisallowConcurrentExecution] to jobs that must not overlap.
Background
Why it exists, and what it was reacting to.
A port of Java's Quartz, it is the choice when scheduling needs are genuinely complex — calendars, misfire policies and clustered execution that simpler schedulers do not address.
