DEV Community

Emin Vergil
Emin Vergil

Posted on • Updated on

background jobs in dotnet

What are Background Jobs?

Background jobs are tasks that run "behind the scenes" in your application. They handle work that doesn't need to happen immediately or that might take a long time to complete.

Why Use Background Jobs?

  1. Better Performance: Your app stays fast because heavy tasks run separately.
  2. Improved User Experience: Users don't have to wait for long tasks to finish.
  3. Reliability: If a background job fails, it doesn't crash your whole application.

How to Implement Background Jobs in .NET

.NET offers several ways to create background jobs:

  1. IHostedService
  2. BackgroundService
  3. Hangfire: A popular third-party library for more complex job scheduling.

A Simple Example

Here's a basic example using BackgroundService:

public class MyBackgroundJob : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            // Do some work here
            await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

This job will run every 5 minutes until the application shuts down.

Cron job example using IHostedService

public abstract class CronJobService : IHostedService, IDisposable
{
    private System.Timers.Timer _timer;
    private readonly CronExpression _expression;
    private readonly TimeZoneInfo _timeZoneInfo;

    protected CronJobService(string cronExpression, TimeZoneInfo timeZoneInfo)
    {
        _expression = CronExpression.Parse(cronExpression);
        _timeZoneInfo = timeZoneInfo;
    }

    public virtual async Task StartAsync(CancellationToken cancellationToken)
    {
        await ScheduleJob(cancellationToken);
    }

    protected virtual async Task ScheduleJob(CancellationToken cancellationToken)
    {
        var next = _expression.GetNextOccurrence(DateTimeOffset.Now, _timeZoneInfo);
        if (next.HasValue)
        {
            var delay = next.Value - DateTimeOffset.Now;
            if (delay.TotalMilliseconds <= 0)   // prevent non-positive values from being passed into Timer
            {
                await ScheduleJob(cancellationToken);
            }
            _timer = new System.Timers.Timer(delay.TotalMilliseconds);
            _timer.Elapsed += async (sender, args) =>
            {
                _timer.Dispose();  // reset and dispose timer
                _timer = null;

                if (!cancellationToken.IsCancellationRequested)
                {
                    await DoWork(cancellationToken);
                }

                if (!cancellationToken.IsCancellationRequested)
                {
                    await ScheduleJob(cancellationToken);    // reschedule next
                }
            };
            _timer.Start();
        }
        await Task.CompletedTask;
    }

    public virtual async Task DoWork(CancellationToken cancellationToken)
    {
        await Task.Delay(5000, cancellationToken);  // do the work
    }

    public virtual async Task StopAsync(CancellationToken cancellationToken)
    {
        _timer?.Stop();
        await Task.CompletedTask;
    }

    public virtual void Dispose()
    {
        _timer?.Dispose();
    }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)