Scheduling Cron Jobs in ASP.NET 6 and C#

July 5th, 2022

This article covers cron jobs and how we can leverage Quartz.Net to schedule cron jobs in ASP.NET 6. It shows how we can store messages in a PostgreSQL database using Devart for PostgreSql, Quartz.Net, and ASP.NET 6.

Pre-requisites

To deal with code examples, you’ll need the following tools:

  • Visual Studio 2022 Community Edition
  • Quartz.Net
  • dotConnect for PostgreSQL

You can download PostgreSQL from here: https://www.postgresql.org/download/

What is Quartz.NET and why is it required?

Quartz.NET is a free and open source.NET port of its Java Quartz Scheduling framework counterpart. It has been used for a long time and offers good support for dealing with cron expressions. Quartz.NET can be used in various .NET applications, including ASP.NET 6 web applications. You can use Quartz.NET to schedule multiple tasks, such as running background jobs, sending email notifications, or executing database maintenance tasks.

One of the great things about Quartz.NET is that it is easy to configure and use. You can create a job class and then a scheduler and trigger and get started in a few lines of code. We’ll learn job, scheduler, and trigger in the next section. Quartz.NET also has a rich set of features, such as support for firing jobs on a cron schedule or executing jobs in response to events. In the following sections, we’ll learn how to leverage Quartz.NET to schedule background jobs in ASP.NET 6 applications.

Understanding Jobs, Triggers, and Schedules

Jobs, triggers, and schedulers are the three core ideas of Quartz.NET. A job includes the code needed to carry out a task or job. Jobs in Quartz.NET are represented by C# classes that implement the IJob interface. Triggers specify how a job should be executed, i.e., on what schedule. The scheduler is responsible for polling and running jobs according to predefined schedules.

Create a new ASP.NET 6 Core Web API Project

In this section, we’ll learn how to create a new ASP.NET 6 Core Web API project in Visual Studio 2022. 

Now, follow the steps outlined below:

1. Open Visual Studio 2022.
2. Click Create a new project.
3. Select ASP.NET Core Web API and click Next.
4. Specify the project name and location to store that project in your system. Optionally, checkmark the Place solution and project in the same directory checkbox.
5. Click Next.
6. In the Additional Information window, select .NET 6.0 (Long-term support) as the project version.

ASP.NET Core Web API

7. Disable the Configure for HTTPS and Enable Docker Support options (uncheck them).
8. Since we’ll not be using authentication in this example, select the Authentication type as None.
9. Since we won’t use Open API in this example, deselect the Enable OpenAPI support checkbox.
10. Since we’ll not be using minimal APIs in this example, ensure that the Use controllers (uncheck to use minimal APIs) is checked.
11. Leave the Do not use top-level statements checkbox unchecked.
12. Click Create to finish the process.

We’ll use this project in this article.

Install NuGet Packages

Before you get started building the framework, you should install dotConnect for PostgreSQL, Quartz, and Quartz.Extensions.Hosting packages in your project. You can install them either from the NuGet Package Manager tool inside Visual Studio or, from the NuGet Package Manager console using the following command:

PM> Install-Package Devart.Data.PostgreSql
PM> Install-Package Quartz
PM> Install-Package Quartz.Extensions.Hosting

Creating a Quartz.Net Job

To write a Quartz.NET job, you must first create a class that implements the IJob interface. This interface contains a single Execute method, which is where you should write your custom code.

This code will be executed when the Quartz.NET scheduler runs your job. You can configure the scheduler to run your job at any interval you desire. For example, you could configure it to run your job every minute, every hour, or every day.

Once you have written your code and configured the scheduler, your job will be executed as scheduled. Now, create a new class named DemoJob in a file named DemoJob.cs with the following code in there:

using Quartz;

namespace QuartzNetDemo
{
    public class DemoJob : IJob
    {
        public Task Execute(IJobExecutionContext context)
        {
            //Write your custom code here
            return Task.FromResult(true);
        }
    }
}

The DemoJob class extends the IJob interface and implements the Execute method. You can use the following code to write a text message into a PostgreSQL database table named Demo:

string message = "This job will be executed again at: " + context.NextFireTimeUtc.ToString();

try
{
      using (PgSqlConnection pgSqlConnection =
       new PgSqlConnection("User Id = postgres; Password = sa123#;" +
                "host=localhost;database=postgres;"))
       {
           using (PgSqlCommand cmd = new PgSqlCommand())
           {
                        cmd.CommandText = "INSERT INTO public.demo " +
                            "(message) VALUES (@message)";

                        cmd.Connection = pgSqlConnection;
                        cmd.Parameters.AddWithValue("id", id);
                        cmd.Parameters.AddWithValue("name", message);

                        if (pgSqlConnection.State != 
                            System.Data.ConnectionState.Open)
                            pgSqlConnection.Open();
                        cmd.ExecuteNonQuery();
           }
       }
}
catch
{
    throw;
}

In this example, we’ve leveraged dotConnect for PostgreSQL, a high-performance object-relational mapper (ORM) for PostgreSQL, for connecting to and working with the PostgreSQL database. The above code when executed will insert a text message that contains the next execution time of the job. Here’s the complete source code of your job class:

using Devart.Data.PostgreSql;
using Quartz;

namespace QuartzNetDemo
{
    public class DemoJob : IJob
    {
        public Task Execute(IJobExecutionContext context)
        {
            string id = Guid.NewGuid().ToString();
            string message = "This job will be executed again at: " + 
            context.NextFireTimeUtc.ToString();

            try
            {
                using (PgSqlConnection pgSqlConnection =
                new PgSqlConnection("User Id = postgres; Password = sa123#;" +
                "host=localhost;database=postgres;"))
                {
                    using (PgSqlCommand cmd = new PgSqlCommand())
                    {
                        cmd.CommandText = "INSERT INTO public.demo " +
                            "(id, message) VALUES(@id, @message)";

                        cmd.Connection = pgSqlConnection;
                        cmd.Parameters.AddWithValue("id", id);
                        cmd.Parameters.AddWithValue("name", message);

                        if (pgSqlConnection.State != System.Data.ConnectionState.Open)
                            pgSqlConnection.Open();
                        cmd.ExecuteNonQuery();
                    }
                }
            }
            catch
            {
                throw;
            }

            return Task.CompletedTask;
        }
    }
}

Configure the Job in the Program Class

You should add the Quartz service to the services contained in the Program.cs file. Here’s where you can also mention the trigger associated with this job and the schedule. You should also add Quartz as a hosted service.

builder.Services.AddQuartz(q =>
{
    q.UseMicrosoftDependencyInjectionScopedJobFactory();
    var jobKey = new JobKey("DemoJob");
    q.AddJob<DemoJob>(opts => opts.WithIdentity(jobKey));

    q.AddTrigger(opts => opts
        .ForJob(jobKey)
        .WithIdentity("DemoJob-trigger")
        .WithCronSchedule("0/5 * * * * ?"));

});

builder.Services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);

Note how a unique key has been created for the job. The trigger mentioned here would run every 5 seconds as specified in the cron expression.

When you run the application, you’ll observe that a text message containing the next execution time of the trigger will be stored in the database table every 5 seconds.

Monitoring a Quartz.Net Job

When you create a job in Quartz.NET, you can specify how often the job should run. You can also specify whether the job should run on a schedule, or if it should be triggered by an event.

If you want to monitor a Quartz.NET job, you can do so using the JobListener interface. This interface provides methods that are called when a job is started, paused, resumed, stopped, or completed. By implementing this interface, you can receive notifications about the status of a Quartz.NET job.

The JobListener interface is just one way to monitor a Quartz.ET job. You can also use the SchedulerListener interface to receive notifications about events that occur within the scheduler itself. This interface provides methods that are called when a scheduler is started, paused, resumed, or shut down. By implementing this interface, you can receive notifications about the status of the Quartz.NET scheduler.

Alternatives to Quartz.NET

There are a few alternatives to Quartz.NET that you may want to consider. One alternative is Hangfire, an open-source library that allows developers to schedule and manage background jobs in .NET and .NET Core applications. Another alternative is to create background jobs is by using the IHostedService interface.

Summary

Quartz.NET is a great choice for scheduling jobs in ASP.NET Core 6, but you need to be careful when using it in a production environment as it might not scale well if your application has too many jobs running. In this article, I’ve demonstrated how we can use Quartz with ASP.NET 6. Note that you can change the content of the job class and write your implementation according to your requirements.

Comments are closed.