Поиск  
Always will be ready notify the world about expectations as easy as possible: job change page
Aug 26

Scheduling tasks with Cronos in .NET

Scheduling tasks with Cronos in .NET
Автор:
Источник:
Просмотров:
1002

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.

Похожее
May 6
Author: Trevor McCubbin
Introduction As a passionate .NET developer, I find myself continuously seeking opportunities to expand my understanding of the .NET ecosystem. In this, my first article ever, I embark on a journey to explore the intricacies of .NET performance, with a...
Dec 1, 2023
Author: Rico Fritzsche
The flight monitor case study: Applying Vertical Slices in a real-world scenario In my last articles I talked about Vertical Slice Architecture and how to organize code in a non-technical way. I showed some simple examples of how to do...
Jul 22
Author: Gabriele Tronchin
Introduction One common way to speed up our applications is by introducing a cache. Typically, the first option that comes to mind is using a MemoryCache (RAM) to save some data in order to speed up retrieval. This approach works...
Aug 27
Author: Merwan Chinta
Introduction Minimal APIs in .NET 8 make a grand entrance, redefining the way we build web services. If you’re curious about what makes Minimal APIs tick and how they can streamline your development process, let’s dive in with some engaging...
Написать сообщение
Тип
Почта
Имя
*Сообщение
RSS