Introduction
While basic timer mechanisms can handle simple periodic operations, enterprise-level applications often require a more robust framework for managing complex background jobs. Quartz.Net is a powerful, open-source job scheduling library ported from the Java Quartz framework. It allows developers to reliably execute tasks such as email dispatches, data synchronization, and system maintainance without relying on external tools like the Windows Task Scheduler. This guide demonstrates how to integrate Quartz.Net into an ASP.NET WebAPI project to manage recurring jobs efficiently.
Installation
To get started, install the Quartz.Net library via the NuGet Package Manager. In your WebAPI project, run the following command in the Package Manager Console:
Install-Package Quartz
This installation automatically adds the necessary dependencies, inlcuding the core Quartz library and required logging adapters.
Creating a Scheduled Job Programmatically
In this example, we will create a job that sends a notification email every 30 minutes.
1. Defining the Job Logic
First, define the business logic that needs to be executed. Here, we encapsulate the email sending logic within an asynchronous method.
public async Task<int> SendNotificationAsync()
{
int statusCode = 0;
try
{
var emailDetails = new EmailMessage
{
Recipient = "user@example.com",
Subject = "System Notification",
Body = "This is an automated scheduled message.",
SenderCredentials = "password"
};
// Assume EmailService is a helper class for sending emails
EmailService.Send(emailDetails);
}
catch (Exception ex)
{
// Log exception details here
statusCode = -1;
}
return await Task.FromResult(statusCode);
}
2. Implementing the Job Interface
Create a class that implements the IJob interface provided by Quartz. The Execute method contains the code that runs when the trigger fires.
using Quartz;
using Quartz.Impl;
public class NotificationJob : IJob
{
public Task Execute(IJobExecutionContext context)
{
// Invoke the business logic
return SendNotificationAsync();
}
}
3. Configuring the Scheduler
The scheduler is responsible for coordinating jobs and triggers. The following class sets up a scheduler that runs the NotificationJob every 30 minutes.
public class SchedulerSetup
{
public static void Initialize()
{
// 1. Create the scheduler factory
ISchedulerFactory factory = new StdSchedulerFactory();
// 2. Get a scheduler
IScheduler scheduler = await factory.GetScheduler();
// 3. Define the job and link it to our NotificationJob class
IJobDetail jobDetail = JobBuilder.Create<NotificationJob>()
.WithIdentity("emailNotification", "group1")
.Build();
// 4. Define the trigger (run every 30 minutes)
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.StartNow()
.WithSimpleSchedule(x => x
.WithIntervalInMinutes(30)
.RepeatForever())
.Build();
// 5. Schedule the job
await scheduler.ScheduleJob(jobDetail, trigger);
// 6. Start the scheduler
await scheduler.Start();
}
}
4. Starting the Scheduler on Application Startup
To ensure the scheduler starts when the application launches, call the initialization method in Global.asax.cs.
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// Standard WebAPI registration
GlobalConfiguration.Configure(WebApiConfig.Register);
// Initialize the Quartz scheduler
SchedulerSetup.Initialize();
}
}
Configuring Jobs via XML
Hardcoding schedules in C# requires recompiling the application to change intervals. Quartz.Net supports XML-based configuration, allowing you to modify schedules externally.
1. Update Web.config
Add the configuration section handler and the Quartz-specific settings to your Web.config file.
<configSections>
<section name="quartz" type="System.Configuration.NameValueSectionHandler"/>
</configSections>
<quartz>
<add key="quartz.scheduler.instanceName" value="DefaultQuartzScheduler"/>
<add key="quartz.threadPool.type" value="Quartz.Simpl.SimpleThreadPool, Quartz"/>
<add key="quartz.threadPool.threadCount" value="10"/>
<add key="quartz.jobStore.type" value="Quartz.Simpl.RAMJobStore, Quartz"/>
<!-- XML Plugin configuration -->
<add key="quartz.plugin.xml.type" value="Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz" />
<add key="quartz.plugin.xml.fileNames" value="~/quartz_jobs.xml"/>
</quartz>
2. Create quartz_jobs.xml
Create a file named quartz_jobs.xml in your project root. This file defines the job and its schedule using a Cron expression.
<job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="2.0">
<schedule>
<!-- Job Definition -->
<job>
<name>EmailNotificationJob</name>
<group>NotificationGroup</group>
<description>Sends periodic emails</description>
<job-type>WebAPI.Utilities.NotificationJob, WebAPI</job-type>
<durable>true</durable>
<recover>false</recover>
</job>
<!-- Trigger Definition using Cron -->
<trigger>
<cron>
<name>IntervalTrigger</name>
<group>NotificationGroup</group>
<description>Runs every 30 minutes</description>
<job-name>EmailNotificationJob</job-name>
<job-group>NotificationGroup</job-group>
<!-- Cron expression: 0 seconds, 0-30 minutes, every hour, every day -->
<cron-expression>0 */30 * * * ?</cron-expression>
</cron>
</trigger>
</schedule>
</job-scheduling-data>
3. Simplify the Scheduler Code
With the configuration moved to XML, the C# scheduler code simplifies to just starting the scheduler.
public class SchedulerSetup
{
public static async Task Initialize()
{
ISchedulerFactory factory = new StdSchedulerFactory();
IScheduler scheduler = await factory.GetScheduler();
// The XML plugin will load the jobs and triggers automatically
await scheduler.Start();
}
}
Advanced Usage with Cron Expressions
For complex scheduling requirements, Cron triggers are preferred over simple interval triggers. A Cron expression consists of seven fields: seconds, minutes, hours, day of month, month, day of week, and year (optional).
Cron Expression Syntax
| Field | Allowed Values | Special Characters |
|---|---|---|
| Seconds | 0-59 | , - * / |
| Minutes | 0-59 | , - * / |
| Hours | 0-23 | , - * / |
| Day of Month | 1-31 | , - * ? / L W |
| Month | 1-12 or JAN-DEC | , - * / |
| Day of Week | 1-7 or SUN-SAT | , - * ? / L # |
| Year (Optional) | 1970-2099 | , - * / |
Special Characters Explained
| Character | Description |
|---|---|
| * | Wildcard (matches all values). e.g., "*" in minutes means "every minute". |
| ? | No specific value (used for Day of Month and Day of Week). |
| - | Range. e.g., "10-12" in hours means 10, 11, and 12. |
| , | List. e.g., "MON,WED,FRI" for Monday, Wednesday, Friday. |
| / | Increment. e.g., "0/15" in seconds means every 15 seconds starting at 0. |
| L | Last. "L" in Day of Month means the last day of the month. |
| W | Weekday. Nearest weekday to the given date. |
| # | Nth X day of the month. e.g., "6#3" is the third Friday. |
Cron Expression Examples
| Expression | Meaning |
|---|---|
| 0 0 12 * * ? | Fire at 12:00 PM every day. |
| 0 15 10 ? * * | Fire at 10:15 AM every day. |
| 0 0/5 14 * * ? | Fire every 5 minutes starting at 2:00 PM and ending at 2:55 PM, every day. |
| 0 15 10 ? * MON-FRI | Fire at 10:15 AM, Monday through Friday. |
| 0 15 10 15 * ? | Fire at 10:15 AM on the 15th day of every month. |
| 0 15 10 L * ? | Fire at 10:15 AM on the last day of every month. |
| 0 0 12 1/5 * ? | Fire at 12:00 PM, every 5 days starting on the 1st of the month. |
Logging
Monitoring job execution is critical for maintenance. Quartz.Net utilizes Common.Logging for internal operations. Developers can integrate this with frameworks like Log4Net or NLog to persist execution logs to files or a database for auditing and debugging purposes.