Search code examples
c#asp.net-corescheduled-tasksasp.net-core-hosted-services

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). For example, if we receive this date and time from user :

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?

Thank you in advance.


Solution

  • 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:

    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.

    You could read more about background tasks with hosted services in ASP.NET Core here from official Microsoft docs:

    https://learn.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:

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