Quartz.NET runs background jobs on schedules you configure. Use it when a repeating timer is too limited—for example, when a task must run at a particular time or multiple application instances must share scheduled work. It can run in a worker service or an ASP.NET Core application. section 34.3 section 34.3.1
Keep the task separate from its schedule:
- A job implements IJob and puts its task logic in Execute. section 34.3.1 section 34.4
- A trigger determines when the job runs. One job can have multiple triggers. section 34.3
- The job factory creates job instances. Dependency-injection integration lets each execution receive a new instance and scope, including scoped dependencies through constructor injection. section 34.3 section 34.3.2
To start in a .NET 7 worker service, add the Quartz.Extensions.Hosting package, call AddQuartz() to configure Quartz.NET, and register AddQuartzHostedService(). Set WaitForJobsToComplete to true if shutdown should wait for running jobs. The hosted service starts the scheduler with the application; without registered jobs or triggers, it has nothing to run. section 34.3.1
Schedule an exchange-rate update
Create an IJob that downloads rates through a typed client and saves them through an EF Core DbContext. Register it with AddJob<T>() under a JobKey, then use AddTrigger() for that key. A simple trigger can start immediately and repeat every five minutes; a cron-based trigger can instead schedule a particular day and time. The job contains the update logic, not the timing rules. section 34.3.2 section 34.4
Check what happens when a run takes longer than its trigger interval: triggers can start concurrent instances of the same job. Add [DisallowConcurrentExecution] when another instance should not begin while one is running. section 34.4
The default scheduler keeps jobs and schedules in memory and is not clustered. If several application instances each run an ordinary BackgroundService, each instance repeats the task. For shared scheduled work, configure Quartz.NET database persistence and clustering so instances coordinate and only one handles a trigger at a time. Keeping a single separate worker service is another option, but it limits that service to one instance. section 34.3.1 section 34.3.3
Listing 34.13 Enabling persistence and clustering for Quartz.NET
using Quartz;
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) => #1
{
var connectionString = Configuration #2
.GetConnectionString("DefaultConnection"); #2
services.AddQuartz(q =>
{
q.SchedulerId = "AUTO"; #3
q. UseMicrosoftDependencyInjectionJobFactory();
q.UsePersistentStore(s => #4
{
s.UseSqlServer(connectionString); #5
s.UseClustering(); #6
s.UseProperties = true; #7
s.UseJsonSerializer(); #7
});
var jobKey = new JobKey("Update_exchange_rates");
q.AddJob<UpdateExchangeRatesJob>(opts =>
opts.WithIdentity(jobKey));
q.AddTrigger(opts => opts
.ForJob(jobKey)
.WithIdentity(jobKey.Name + " trigger")
.StartNow()
.WithSimpleSchedule(x => x
.WithInterval(TimeSpan.FromMinutes(5))
.RepeatForever())
);
});
services.AddQuartzHostedService(
q => q.WaitForJobsToComplete = true);
})
.Build();
host.Run();
#1 Configuration is identical for both ASP.NET Core apps and worker services.
#2 Obtains the connection string for your database from configuration
#3 Each instance of your app must have a unique SchedulerId. AUTO takes care of this for you.
#4 Enables database persistence for the Quartz.NET scheduler data
#5 Stores the scheduler data in a SQL Server (or LocalDb) database
#6 Enables clustering between multiple instances of your app
#7 Adds the recommended configuration for job persistence
Inspect the persistence configuration after the single-instance setup: it supplies a database connection, enables SQL Server storage, and calls UseClustering(). section 34.3.3
