简体   繁体   English

在 ASP.NET Core 3.1 中,如何在未来的特定日期和时间安排带有托管服务的后台任务(Cron 作业)?

[英]In ASP.NET Core 3.1, how can I schedule a background task (Cron Jobs) with hosted services for a specific date and time in the future?

I am working on a project based on ASP.NET Core 3.1 and I want to add a specific functionality to it to schedule publishing a post in the future in a date and time specified by post author (something like what Wordpress does for scheduled posts through its cron jobs).我正在开发一个基于 ASP.NET Core 3.1 的项目,我想向它添加一个特定的功能,以安排将来在帖子作者指定的日期和时间发布帖子(类似于 Wordpress 通过它的 cron 作业)。 For example, if we receive this date and time from user :例如,如果我们从用户那里收到这个日期和时间:

2020-09-07 14:08:07 2020-09-07 14:08:07

Then, how can I schedule a background task for it by using hosted services to run only for one time and to change a flag in database and save changes after that?那么,如何通过使用托管服务仅运行一次并更改数据库中的标志并在此之后保存更改来为其安排后台任务?

I've read some articles about it but they didn't specify date and time and just mentioned repeated tasks for every 5 second and stuff like that with cron expressions, but, the thing I need to know is how can I schedule a background task for a specific date and time?我已经阅读了一些关于它的文章,但他们没有指定日期和时间,只是提到了每 5 秒重复一次的任务以及类似 cron 表达式的内容,但是,我需要知道的是如何安排后台任务特定日期和时间?

Thank you in advance.先感谢您。

I combined CrontabSchedule with IHostedService.我将 CrontabSchedule 与 IHostedService 结合起来。 The implementation below is lightweight (no architecture imposing libs) and no polling.下面的实现是轻量级的(没有强加库的架构)并且没有轮询。

public class SomeScheduledService: IHostedService
{
    private readonly CrontabSchedule _crontabSchedule;
    private DateTime _nextRun;
    private const string Schedule = "0 0 1 * * *"; // run day at 1 am
    private readonly SomeTask _task;

    public SomeScheduledService(SomeTask task)
    {
        _task = Task;
        _crontabSchedule = CrontabSchedule.Parse(Schedule, new CrontabSchedule.ParseOptions{IncludingSeconds = true});
        _nextRun = _crontabSchedule.GetNextOccurrence(DateTime.Now);
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        Task.Run(async () =>
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                await Task.Delay(UntilNextExecution(), cancellationToken); // wait until next time

                await _task.Execute(); //execute some task

                _nextRun = _crontabSchedule.GetNextOccurrence(DateTime.Now);
            }
        }, cancellationToken);

        return Task.CompletedTask;
    }

    private int UntilNextExecution() => Math.Max(0, (int)_nextRun.Subtract(DateTime.Now).TotalMilliseconds);

    public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

I want to add a specific functionality to it to schedule publishing a post in the future in a date and time specified by post author.For example, if we receive this date and time from user : 2020-09-07 14:08:07 .我想给它添加一个特定的功能,以安排将来在帖子作者指定的日期和时间发布帖子。例如,如果我们从用户那里收到这个日期和时间:2020-09-07 14:08:07 .

Then, how can I schedule a background task for it by using hosted services to run only for one time and to change a flag in database and save changes after that?那么,如何通过使用托管服务仅运行一次并更改数据库中的标志并在此之后保存更改来为其安排后台任务?

It seems that you'd like to execute a background task/job at a user specified datetime, to achieve the requirement, you can try to use some message queue services, such as Azure Queue Storage , which enable us to specify how long the message should be invisible to Dequeue and Peek operations by setting visibilityTimeout .看来您想在用户指定的日期时间执行后台任务/作业,为了达到要求,您可以尝试使用一些消息队列服务,例如Azure Queue Storage ,它使我们能够指定消息的长度通过设置visibilityTimeout应该对Dequeue 和Peek 操作不visibilityTimeout

While your application user want to create a new post and specify a publishing date time, you can insert a new message (with specified visibilityTimeout based on user expected datetime) into the queue, so that this new inserted message would be only visible at specified date time in the queue.当您的应用程序用户想要创建一个新帖子并指定发布日期时间时,您可以将一条新消息(根据用户预期的日期时间具有指定的可见性超时)插入队列,以便此新插入的消息仅在指定日期可见排队的时间。

QueueClient theQueue = new QueueClient(connectionString, "mystoragequeue");

if (null != await theQueue.CreateIfNotExistsAsync())
{
    //The queue was created...
}

var newPost = "Post Content Here";

var user_specified_datetime = new DateTime(2020, 9, 9, 20, 50, 25);
var datetime_now = DateTime.Now;

TimeSpan duration = user_specified_datetime.Subtract(datetime_now);

await theQueue.SendMessageAsync(newPost, duration, default); 

Then you can implement a queue triggered background task to retrieve message(s) from the queue and update your database record(s).然后你可以实现一个队列触发的后台任务来从队列中检索消息并更新你的数据库记录。

Note : Microsoft Azure Storage Emulator is a tool that emulates the Azure Queue etc services for local development and testing purposes, you can try to test code against the storage services locally without creating an Azure subscription or incurring any costs.注意Microsoft Azure Storage Emulator是一种模拟 Azure Queue 等服务用于本地开发和测试目的的工具,您可以尝试在本地针对存储服务测试代码,而无需创建 Azure 订阅或产生任何费用。

Use DNTScheduler and set specific date and time使用DNTScheduler并设置特定的日期和时间

        services.AddDNTScheduler(options =>
        {
            // DNTScheduler needs a ping service to keep it alive. Set it to false if you don't need it. Its default value is true.
            // options.AddPingTask = false;

            options.AddScheduledTask<DoBackupTask>(
                runAt: utcNow =>
                {
                    var now = utcNow.AddHours(3.5);
                    return  now.Hour == 14 && now.Minute == 08 && now.Second == 07;
                },
                order: 1);
        });

After some trial and error I found a way to schedule a background task for specific date and time by using hosted service as I asked in the question, and, I did that with System.Threading.Timer and Timespan like this:经过一些反复试验,我找到了一种方法,通过使用我在问题中提出的托管服务来安排特定日期和时间后台任务,并且,我使用System.Threading.TimerTimespan做到了这一点,如下所示:

public class ScheduleTask : IScheduler, IDisposable
{

   private Timer _timer;
   private IBackgroundTaskQueue TaskQueue { get; }

   // Set task to schedule for a specific date and time
    public async Task SetAndQueueTaskAsync(ScheduleTypeEnum scheduleType, DateTime scheduleFor, Guid scheduledItemId)
    {
        // Omitted for simplicity
        // ....

        TaskQueue.QueueBackgroundWorkItem(SetTimer);
    }

   // ......
   // lines omitted for simplicity
   // .....

   // Set timer for schedule item
   private Task SetTimer(CancellationToken stoppingToken)
   {
      // ......
      // lines omitted for simplicity
      // .....

      _timer = new Timer(DoWork, null, (item.ScheduledFor - DateTime.UtcNow).Duration(), TimeSpan.Zero);


      return Task.CompletedTask;
   }

   private void DoWork(object state)
   {
       ScheduledItemChangeState(DateTime.UtcNow).Wait();
   }

   // Changes after the scheduled time comes
   private async Task ScheduledItemChangeState(DateTime scheduleFor)
   {
       using (var scope = Services.CreateScope())
       {
           var context =
            scope.ServiceProvider
                .GetRequiredService<DataContext>();

          // Changing some data in database
       }
    }

   public void Dispose()
   {
      _timer?.Dispose();
   }
}

If you look at the part of the above code in which I passed (item.ScheduledFor - DateTime.UtcNow) as Timer class constructor's third parameter to initialize a new instance of it, I actually ask the timer to do a specific work in a specific time I stored as a DateTime in item.ScheduledFor .如果您查看上面代码中我通过(item.ScheduledFor - DateTime.UtcNow)作为 Timer 类构造函数的第三个参数来初始化它的新实例的部分,我实际上要求计时器在特定的我在item.ScheduledFor存储为 DateTime 的item.ScheduledFor

You could read more about background tasks with hosted services in ASP.NET Core here from official Microsoft docs:您可以从 Microsoft 官方文档中阅读有关ASP.NET Core 中托管服务的后台任务的更多信息:

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio

To see the full implementation in my Github repo which has the possibility to recover the scheduled tasks from database after restarting the server, use the following link:要查看我的 Github 存储库中的完整实现,它有可能在重新启动服务器后从数据库中恢复计划任务,请使用以下链接:

https://github.com/aspian-io/aspian/tree/master/Infrastructure/Schedule https://github.com/aspian-io/aspian/tree/master/Infrastructure/Schedule

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 ASP.NET Core中托管服务的并行排队后台任务 - Parallel queued background tasks with hosted services in ASP.NET Core 具有托管服务的基于ASP.NET Core事件的后台任务 - ASP.NET Core event based background tasks with hosted services 如何在共享服务器上托管的 ASP.Net Core 3.1 应用程序中存储生产机密,如连接字符串 - How can I store production secrets like connection string in ASP.Net Core 3.1 application, hosted on a shared server 如何调度数据库任务 ASP.NET Core? - How to schedule a database task ASP.NET Core? ASP.NET 核心 3.1 托管服务针对给定的计划运行多次(它应该每天运行一次) - ASP.NET Core 3.1 Hosted Service runs multiple times for a given schedule (its supposed to run once a day) 如何让用 ASP.Net Core 2.0 编写的自定义异常处理程序在 ASP.Net Core 3.1 中工作? - How can I get a custom exception handler written in ASP.Net Core 2.0 to work in ASP.Net Core 3.1? 如何在asp.net core 3.1中使用asp.net framework 4.8中的StatisticFormula类 - How can I use a StatisticFormula Class from asp.net framework 4.8 in asp.net core 3.1 在 ASP.NET Core 3.1 中为 Singleton 使用多项服务 - Use Multiple Services for Singleton in ASP.NET Core 3.1 Asp.net 核心 3.1- 我如何从 Controller 返回到 Razor 页面? - Asp.net Core 3.1- How can I return from Controller to Razor Page? 如何在 ASP.NET Core 3.1 WebApi 中使用自定义记录器登录一些不同的文件? - How can i log in some different files with custom logger in ASP.NET Core 3.1 WebApi?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM