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?
- Better Performance: Your app stays fast because heavy tasks run separately.
- Improved User Experience: Users don't have to wait for long tasks to finish.
- 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:
- IHostedService
- BackgroundService
- 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);
}
}
}
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();
}
}
Top comments (0)