liveBook · Manning

Quartz.NET

As explained in ASP.NET Core in Action, 3e

Quartz.NET is an open-source .NET job-scheduling library that runs tasks according to configured triggers and supports clustered execution.

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

How ASP.NET Core in Action, 3e covers it

How deep it goes
Works through hosted-service setup, job registration, triggers, dependency-injection lifetimes, and database-backed clustering; does not show scheduler internals.
Examples
Uses an exchange-rate download saved with EF Core, an immediate five-minute trigger, and a day-and-time cron trigger.
What it assumes
Starts with a .NET application and shows package installation and scheduler registration while using generic-host and dependency-injection APIs.
What you can do after
Apply it via Quartz.NET to register scheduled jobs and configure database-backed clustering for multiple application instances.
How it connects
Connects IHostedService, BackgroundService, dependency injection, IJob, triggers, cron schedules, database persistence, and clustering.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of ASP.NET Core in Action, 3e. The section links point to the parts of the book it draws on.