在 .Net Core 3.0 中每天午夜运行后台服务

9
我希望每晚都能运行一个后台服务。.NET Core默认的BackgroundService在task.delay上运行,我希望每天在午夜运行(24小时间隔)。
问题在于BackgroundService会在每个task.Delay间隔上运行,而不是指定的特定时间。
public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;
        private readonly IServiceScopeFactory _serviceScopeFactory;

        public Worker(ILogger<Worker> logger, IServiceScopeFactory serviceScopeFactory)
        {
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory));
        }
        protected override async Task ExecuteAsync(CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                // We ultimately resolve the actual services we use from the scope we create below.
                // This ensures that all services that were registered with services.AddScoped<T>()
                // will be disposed at the end of the service scope (the current iteration).
                using var scope = _serviceScopeFactory.CreateScope();

                var configuration = scope.ServiceProvider.GetRequiredService<IWorkFlowScheduleService>();
                configuration.DailySchedule(dateTime: DateTime.Now);

                _logger.LogInformation($"Sending message to ");

                await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken);
            }
        }
    }

10
不要将其制作成标准的控制台应用程序并通过平台的调度程序运行(例如Linux的crontab)。较低的复杂性将是您以后感激自己的事情。 - user32826
你计划在哪个操作系统上安排这个任务? - Clint
也许这可以帮到你?https://stackoverflow.com/questions/58535975/how-can-i-order-asp-net-core-runs-a-method-once-per-day/58537020#58537020 - Tony Ngo
@Clint 我正在Windows上运行这个程序。 - pratik pokharel
2
为什么不在Windows任务计划程序中安排它,并在特定的“DateTime”和“x”间隔时间执行预定的部分呢? - Clint
5个回答

11

这是我的解决方案。

 protected override async Task ExecuteAsync(CancellationToken stoppingToken)
 {
     do
     {
         int hourSpan = 24 - DateTime.Now.Hour;
         int numberOfHours = hourSpan;

         if (hourSpan == 24)
         {
             //do something
             numberOfHours = 24;
         }

         await Task.Delay(TimeSpan.FromHours(numberOfHours), stoppingToken);
     }
     while (!stoppingToken.IsCancellationRequested);
 }

9

把一个初始的Task.Delay放在那里等到午夜,然后做你需要做的事情,之后再Task.Delay 24小时。

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    // calculate seconds till midnight
    var now = DateTime.Now;
    var hours = 23 - now.Hour;
    var minutes = 59 - now.Minute;
    var seconds = 59 - now.Second;
    var secondsTillMidnight = hours * 3600 + minutes * 60 + seconds;

    // wait till midnight
    await Task.Delay(TimeSpan.FromSeconds(secondsTillMidnight), stoppingToken);

    while (!stoppingToken.IsCancellationRequested)
    {
        // do stuff
        _logger.LogInformation($"Sending message to ");

        // wait 24 hours
        await Task.Delay(TimeSpan.FromHours(24), stoppingToken);
    }
}

这是会有偏移的,尤其是如果“东西”需要花费较长时间的话。 - Luke

2

这里有另一个可能的解决方案。

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    do
    {
        var waitTime = new TimeSpan(23, 59, 59) - DateTime.Now.TimeOfDay;

        await Task.Delay(waitTime, stoppingToken);

        //Do Work

    } while (!cancellationToken.IsCancellationRequested);
}

0

我使用NCrontab 3.3.1

https://www.nuget.org/packages/ncrontab/

这样,您可以使用cron;否则,我同意早期评论之一,建议创建一个控制台应用程序,并将其作为cron或服务直接在您的服务器上运行。

这是我实现NCrontab的一个例子。

public class YourServiceName : BackgroundService
{
    private CrontabSchedule _schedule;
    private DateTime _nextRun;
    // My CrontabHelper class simply returns a string representing a cron schedule. You can just use 0 0 0 * * * to run every day at midnight instead
    private string Schedule => CrontabHelper.Schedule(CrontabHelper.CronType.Day); 

    public YourServiceName()
    {
        _schedule = CrontabSchedule.Parse(Schedule, new CrontabSchedule.ParseOptions { IncludingSeconds = true });
        _nextRun = _schedule.GetNextOccurrence(DateTime.Now);
    }

    protected async override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        do
        {
            var now = DateTime.Now;
            if (now > _nextRun)
            {
                // DO WORK HERE
            }

            await Task.Delay(5000, stoppingToken);
        }
        while (!stoppingToken.IsCancellationRequested);
    }
}

然后在你的 program.cs 中包含这个

builder.Services.AddHostedService<YourServiceName>();

希望这可以帮到你!

0
如果你使用的是.NET 6.0,这里是你的答案。
public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;
    private readonly IServiceScopeFactory _serviceScopeFactory;

    public Worker(ILogger<Worker> logger, IServiceScopeFactory serviceScopeFactory)
    {
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory));
    }

    protected override async Task ExecuteAsync(CancellationToken cancellationToken)
    {
        using(using PeriodicTimer timer = new PeriodicTimer(TimeSpan.FromHours(24)))
        {
            while (!cancellationToken.IsCancellationRequested && await timer.WaitForNextTickAsync(stoppingToken))
            {
                /*
                * Do whatever you wanted to do here.
                */
                
                _logger.LogInformation($"Sending message to ");
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接