Reputation: 465
In my app I have a handful of BackgroundServices. The problem is, that they run on unscheduled times. Sometimes exactly a day too late, sometimes two days in a row even though it should only run once a week.
This is how one of the BackgroundServices looks like:
using Cronos;
namespace MyApp.HostedServices
{
public class MyFirstService : BackgroundService
{
private const string schedule = "0 1 0 ? * *";
private readonly CronExpression _cron;
public MyFirstService()
{
_cron = CronExpression.Parse(schedule, CronFormat.IncludeSeconds);
}
protected override async Task ExecuteAsync(CancellationToken stopToken)
{
while (!stopToken.IsCancellationRequested)
{
DateTime utcNow = DateTime.UtcNow;
DateTime? nextUtc = _cron.GetNextOccurrence(utcNow);
await Task.Delay(nextUtc.Value - utcNow, stopToken);
if (utcNow.Day == 1) await DoWork();
/*
DateTime now = DateTime.Now;
DateTime start = now.Date.AddMinutes(1);
if (now > start) start = start.AddDays(1);
await Task.Delay(start.Subtract(now), stopToken);
if (now.Day == 1) await DoWork();
*/
}
}
protected async Task DoWork()
{
// The work to be done
}
}
}
Another example:
using Cronos;
namespace MyApp.HostedServices
{
public class MySecondService : BackgroundService
{
private const string schedule = "0 0 13 ? * *";
private readonly CronExpression _cron;
private readonly int[] reminderDays = { 1, 3, 6, 9 };
public MySecondService()
{
_cron = CronExpression.Parse(schedule, CronFormat.IncludeSeconds);
}
protected override async Task ExecuteAsync(CancellationToken stopToken)
{
while (!stopToken.IsCancellationRequested)
{
DateTime utcNow = DateTime.UtcNow;
DateTime? nextUtc = _cron.GetNextOccurrence(utcNow);
await Task.Delay(nextUtc.Value - utcNow, stopToken);
if (reminderDays.Contains(utcNow.Day)) await DoWork();
/*
DateTime now = DateTime.Now;
DateTime start = now.Date.AddHours(13);
if (now > start) start = start.AddDays(1);
await Task.Delay(start.Subtract(now), stopToken);
if (reminderDays.Contains(now.Day)) await DoWork();
*/
}
}
protected async Task DoWork()
{
// The work to be done
}
}
}
I've asked the same question before and was adviced to use cron to calculate the next run instead of doing it myself. At first I thought that did it, but the problem persists.
For example did the MySecondService run both the sixth and seventh this month.
What could be wrong?
Upvotes: 0
Views: 647
Reputation: 457167
I suspect your problem is here:
if (reminderDays.Contains(utcNow.Day))
utcNow
is the time that the scheduled job started its loop. I.e., if it started up on Tuesday and the next trigger time was Wednesday, utcNow
would refer to Tuesday, not Wednesday.
I'm pretty sure you want nextUtc
there instead, which represents the actual time DoWork
executes:
if (reminderDays.Contains(nextUtc.Value.Day))
Upvotes: 2