Scheduled tasks

ScheduledTaskService in core module

public abstract class ScheduledTaskService : IHostedService, IDisposable
{
    private Task _executingTask;
    private readonly CancellationTokenSource _stoppingCts = new();

    public virtual Task StartAsync(CancellationToken cancellationToken)
    {
        // Store the task we're executing
        _executingTask = ExecuteAsync(_stoppingCts.Token);

        // If the task is completed then return it,
        // this will bubble cancellation and failure to the caller
        if (_executingTask.IsCompleted)
        {
            return _executingTask;
        }

        // Otherwise it's running
        return Task.CompletedTask;
    }

    public virtual async Task StopAsync(CancellationToken cancellationToken)
    {
        // Stop called without start
        if (_executingTask == null)
        {
            return;
        }

        try
        {
            // Signal cancellation to the executing method
            _stoppingCts.Cancel();
        }
        finally
        {
            // Wait until the task completes or the stop token triggers
            await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite,
                                                      cancellationToken));
        }
    }

    protected virtual async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        //stoppingToken.Register(() =>
        //        _logger.LogDebug($" GracePeriod background task is stopping."));

        do
        {
            await Process();

            await Task.Delay(5000, stoppingToken); //5 seconds delay
        }
        while (!stoppingToken.IsCancellationRequested);
    }

    protected abstract Task Process();

    public void Dispose()
    {
        GC.SuppressFinalize(this);
    }
}

public abstract class ScheduledTask : ScopedProcessor
{
    private readonly CrontabSchedule _schedule;
    private DateTime _nextRun;
    protected abstract string Schedule { get; }

    public ScheduledTask(IServiceScopeFactory serviceScopeFactory) : base(serviceScopeFactory)
    {
        _schedule = CrontabSchedule.Parse(Schedule);
        _nextRun = _schedule.GetNextOccurrence(DateTime.Now);
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        do
        {
            var now = DateTime.Now;
            _schedule.GetNextOccurrence(now);
            if (now > _nextRun)
            {
                await Process();
                _nextRun = _schedule.GetNextOccurrence(DateTime.Now);
            }
            await Task.Delay(5000, stoppingToken); //5 seconds delay
        }
        while (!stoppingToken.IsCancellationRequested);
    }
}

public abstract class ScopedProcessor : ScheduledTaskService
{
    internal readonly IServiceScopeFactory ServiceScopeFactory;

    public ScopedProcessor(IServiceScopeFactory serviceScopeFactory) : base()
    {
        ServiceScopeFactory = serviceScopeFactory;
    }

    protected override async Task Process()
    {
        using var scope = ServiceScopeFactory.CreateScope();
        await ProcessInScope(scope.ServiceProvider);
    }

    public abstract Task ProcessInScope(IServiceProvider serviceProvider);
}


How to create a scheduled task

Implement the scheduled task:

public class NewsletterScheduledTask : ScheduledTask
{
    public NewsletterScheduledTask(IServiceScopeFactory serviceScopeFactory) : base(serviceScopeFactory) { }

    protected override string Schedule => "0 6 * * *";

    public override Task ProcessInScope(IServiceProvider serviceProvider)
    {
        using var scope = ServiceScopeFactory.CreateScope();
        var newsletterService = scope.ServiceProvider.GetService<INewsletterService>();

        newsletterService.SendNewsletter();

        return Task.CompletedTask;
    }
}

Register the new scheduled task in the services installer:

services.AddSingleton<IHostedService, NewsletterScheduledTask>();


References

results matching ""

    No results matching ""