RU EN
Aug 26, 2024

Scheduling tasks with Cronos in .NET

Scheduling tasks with Cronos in .NET
Author:
Source:
Views:
1662
Scheduling tasks with Cronos in .NET favorites 0

Mastering Scheduled Tasks in .NET with Cronos

Cronos is a task scheduling library for .NET that allows scheduling and executing tasks at specific times or intervals using the CRON pattern.

In this article, I will present how to set up and use task scheduling in the background in a .NET 8 application.

Prerequisites

  • .NET 8.0
  • Visual Studio 2022
  • Nuget package Cronos

Web API

In the Program.cs class of the API, configure as shown below:

using Sample.Scheduler.Core.Extensions;
using Sample.Scheduler.Core.TimerSchedulers;

var builder = WebApplication.CreateBuilder(args);
builder.AddSerilog();

builder.Services.AddRouting(options => options.LowercaseUrls = true);
builder.Services.AddControllers();

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddCronJob<TimerSendEmail>(c => c.CronExpression = @"0 */1 * * * *");
builder.Services.AddCronJob<TimerCheckDatabase>(c => c.CronExpression = @"* * * * * *");

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.MapControllers();

app.Run();

The next step is to create the extension class CronJobExtensions, responsible for controlling the next scheduling of each task using the CronExpression:

public abstract class CronJobExtensions : BackgroundService
{
    private readonly CronExpression _expression;
    private readonly TimeZoneInfo _timeZoneInfo;
    private readonly IServiceProvider _serviceProvider;

    protected CronJobExtensions(string cronExpression, TimeZoneInfo timeZoneInfo, IServiceProvider serviceProvider)
    {
        _expression = CronExpression.Parse(cronExpression, CronFormat.IncludeSeconds);
        _timeZoneInfo = timeZoneInfo;
       _serviceProvider = serviceProvider;
    }

    protected override async Task ExecuteAsync(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            var now = DateTimeOffset.Now;
            var next = _expression.GetNextOccurrence(now, _timeZoneInfo);
            if (!next.HasValue) continue;

            var delay = next.Value - now;
            await Task.Delay(delay, cancellationToken);

            if (cancellationToken.IsCancellationRequested)
                continue;

            try
            {
                using var scope = _serviceProvider.CreateScope();
                await DoWork(scope, cancellationToken);
            }
            catch (Exception ex)
            {
                Log.Error(ex, nameof(ExecuteAsync));
            }
        }
    }

    public abstract Task DoWork(IServiceScope scope, CancellationToken cancellationToken);
}

public interface IScheduleConfig<T>
{
    string CronExpression { get; set; }
    TimeZoneInfo TimeZoneInfo { get; set; }
}

public class ScheduleConfig<T> : IScheduleConfig<T>
{
    public string CronExpression { get; set; }
    public TimeZoneInfo TimeZoneInfo { get; set; } = TimeZoneInfo.Local;
}

Next, it is necessary to create the extension class ScheduledServiceExtensions responsible for registering the schedules:

public static class ScheduledServiceExtensions
{
    public static IServiceCollection AddCronJob<T>(this IServiceCollection services, Action<IScheduleConfig<T>> options) where T : CronJobExtensions
    {
        var config = new ScheduleConfig<T>();
        options.Invoke(config);

        services.AddSingleton<IScheduleConfig<T>>(config);

        services.AddHostedService<T>();

        return services;
    }
}

And finally, let’s create the class for scheduling the actions, in this example, we will create a schedule to be triggered every second and another to be triggered every minute. These two schedules are registered in the Program.cs class

public class TimerCheckDatabase : CronJobExtensions
{
    public TimerCheckDatabase(IScheduleConfig<TimerCheckDatabase> config, IServiceProvider serviceProvider)
  : base(config.CronExpression, config.TimeZoneInfo, serviceProvider)
    {
    }

    public override Task DoWork(IServiceScope scope, CancellationToken cancellationToken)
    {
        Serilog.Log.Information("Verified Database!");
        return Task.CompletedTask;
    }
}

public class TimerSendEmail : CronJobExtensions
{
    public TimerSendEmail(IScheduleConfig<TimerSendEmail> config, IServiceProvider serviceProvider)
  : base(config.CronExpression, config.TimeZoneInfo, serviceProvider)
    {
    }

    public override Task DoWork(IServiceScope scope, CancellationToken cancellationToken)
    {
        Serilog.Log.Information("Email sent!");
        return Task.CompletedTask;
    }
}

Testing

To test the schedules, run the API and analyze the logs generated by Serilog in the application console:

Serilog in the application console

It’s possible to see that the task scheduling that checks the database is triggered every second, while the scheduling that sends emails is triggered every minute.

Conclusion

Cronos is a library for parsing Cron expressions and calculating the next occurrences, with it, it is possible to schedule important background tasks, examples: Perform database maintenance every morning, capture data from a source from time to time, creation of alerts, etc.

Похожее
Jul 25, 2024
Author: Ankit Sahu
Introduction In this article, we are going to discuss How to implement Authentication and Authorization in .NET 8 Web API. This is a continuation of.Net 8 series, so if you are new, Please have a look at my previous articles....
Apr 15, 2024
Author: Olorundara Komolafe
Scheduling one or more background tasks is almost inevitable when building robust, self-sustaining APIs with .NET. A few packages have been around for years, such as Hangfire and Quartz.NET. ASP.NET Core allows background tasks to be implemented as hosted services....
Jul 18, 2024
Author: Ankit Sahu
Introduction Creating a CRUD (Create, Read, Update, Delete) API in .NET 8 with an In-memory collection is a common scenario in web development. In this article, we’ll walk through building a complete .NET 8 Web API with a real-world use...
Feb 3
Author: C# Programming Obinna “Anderson” Asiegbulam
Introduction Open Data Protocol (OData) is a standard protocol for building and consuming RESTful APIs. It extends traditional REST-based data access techniques by providing a uniform way to expose, structure, and manipulate data. Originally developed by Microsoft, OData is widely...
Написать сообщение
Тип
Почта
Имя
*Сообщение