Categories
.NET Core

Setup A Scheduled Repeating Task With .NET Core

In this post, we will learn to setup a scheduled repeating task with .NET Core framework. We will schedule a task that will repeatedly run at our desired time.

Introduction

In a client-server application, a scheduled task or service is a common need. Maybe you need to move your log files to a separate bucket on a daily basis. Or you want to clean up data in your database every weekend.

A scheduled service can be run at your desired time which enables you to move certain tasks when there are least users in your system, reducing impact and load on the server.

How To Create A Repeating Task In .NET Core

There are many ways to create a repeating scheduled task with .NET core framework. In this post, we will learn to create such scheduler in the following steps:

  1. Create a background task with the help of IHostedService.
  2. Use a Timer to setup a repeating task.
  3. Implement a delayed Task to setup a schedule to run task at desired time.

Setup

Let’s say we are looking to create a service that removes inactive users from our system. In order to reduce server impact, we want to run schedule this service at 1 am every night.

Implementing IHostedervice

To create such service, we will first create a class that implements the IHostedervice interface which exposes following two methods:

  • StartAsync: Triggered when the application host is ready to start the service.
public System.Threading.Tasks.Task StartAsync (System.Threading.CancellationToken cancellationToken);
  • StopAsync: Triggered when the application host is performing a graceful shutdown.
public System.Threading.Tasks.Task StopAsync (System.Threading.CancellationToken cancellationToken);

Implementing these two methods, we create our class in this way:

public class AccountRemovalService : IHostedService
{
    private readonly IAccountService accountServive;

    public AccountRemovalService(IAccountService accountServive)
    {
        this.accountServive = accountServive;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        // todo:
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        // todo:
    }

    private void RemoveInactiveAccounts(object state)
    {
        accountServive.DeleteInactiveUserData();
    }
}

Here, AccountRemovalService class is implementing the IHostedService interface. The IAccountService is the service that you will have created implementing the actual business logic for removing accounts.

Add Timer To Repeat Task

Next, we add a Timer which will repeatedly call the RemoveInactiveAccounts method.

The timer will be initialized in the StartAsync method and can be removed if required from StopAsync method.

public Task StartAsync(CancellationToken cancellationToken)
    {
        // timer repeates call to RemoveScheduledAccounts every 24 hours.
        _timer = new Timer(
            RemoveInactiveAccounts, 
            null, 
            TimeSpan.Zero, 
            TimeSpan.FromHours(24)
        );

        return Task.CompletedTask;
    }

/// Call the Stop async method if required from within the app.
    public Task StopAsync(CancellationToken cancellationToken)
    {
        _timer?.Change(Timeout.Infinite, 0);

        return Task.CompletedTask;
    }

In this case, our Timer repeats the call to RemoveInactiveAccounts every 24 hours.

Registering The Hosted Service

To configure the above created service, we need to register with the AddHostedService method in the startup.

services.AddHostedService<AccountRemovalService>();

Now, when you run your program with dotnet run, the hosted service kicks in and it places the AccountRemovalService in motion with the Timer.

Scheduling Timer To Repeat At Desired Time

Right now, although we have setup a repeating task which occurs on a daily basis, there’s a small issue we still have here.

We wanted to run this code at 1 am every night, but right now, it is not scheduled to run exactly at our desired time. In fact, the task runs right after the start of initial program or deployment to the server and repeats after every 24 hours.

So, if we had started the .NET app at 2 pm, our service will have repeated everyday at 2 pm.

So, how to schedule this task to run exactly at 1 am?

Delay Timer Initialization

We will design our solution in the following two steps:

  • Calculate the time difference between the next run time and current time period.
  • Delay the timer initialization until the next run time.

For, calculating the next run time, we can make use of DateTime.Today which returns a date time object with today’s date and time as midnight value.

Then, we will use a delayed Task.

So, our implementation will now look like this:

public Task StartAsync(CancellationToken cancellationToken)
{      
	TimeSpan interval = TimeSpan.FromHours(24);
	//calculate time to run the first time & delay to set the timer
	//DateTime.Today gives time of midnight 00.00
	var nextRunTime = DateTime.Today.AddDays(1).AddHours(1);
	var curTime = DateTime.Now;
	var firstInterval = nextRunTime.Subtract(curTime);

	Action action = () =>
	{
		var t1 = Task.Delay(firstInterval);
		t1.Wait();
		//remove inactive accounts at expected time
		RemoveScheduledAccounts(null);
		//now schedule it to be called every 24 hours for future
		// timer repeates call to RemoveScheduledAccounts every 24 hours.
		_timer = new Timer(
			RemoveScheduledAccounts,
			null,
			TimeSpan.Zero,
			interval
		);
	};

	// no need to await this call here because this task is scheduled to run much much later.
	Task.Run(action);
	return Task.CompletedTask;	
}

Wrapping Up

In this post, we looked at how we can create a scheduled service in a .NET core environment. We initially created a service that can be configured to run after a certain interval. Then we configured it to run at a specific time every day.

You can use the same logic to create scheduled services with .NET as per your need. Good luck.

Checkout our other articles on .NET Core here.

Leave a Reply

Your email address will not be published. Required fields are marked *