Running background tasks in ASP.Net applications
While developing applications, you may come across various occasions where you create background services to achieve automation in your applications. Such services include sending notification, update a user’s subscription expiration or may be changing the state of a workflow activity and so on.
Developers use Windows Service, Cron jobs or Executables that works in a separate thread than your web applications that works based on a schedule. In .Net, for creating a background task, developers mainly use the Console Application or Windows Service template. While this will do the job, it adds some complexity. Few of the issues developers face with background services are below
- You may use configuration files in the web application such as connection string, logs file path etc, however for the console application, you need to redefine them under its application settings, something your operation team will not prefer, of course this adds operations complexity.
- Migrating the application may lead to troubles as I have seen several times, the migration happens without migrating the background service and then developers troubleshoot and identifies they forgot some of the services.
- Modifying the code in the application and database structure may impact the background service.
- And many more
Though it had limitations, developers used it a lot especially when they need to implement background services. Now with the ASP.Net framework has built in capability to run background tasks from the web application itself. Cool, for many of you, it must be music to your ears, now from the same project you can manage your pages, application logic and background services.
Background Service in Asp.Net application
In ASP.Net background tasks can be implemented as hosted services. In this article, I am going to demonstrate how you can develop a background service in ASP.Net
For the purpose of this demo, I used the following environment.
- ASP.Net 6
- Visual Studio 2022 Preview
- ASP.Net Web application
Also, I have a class in my application called Task that has a due date and Is Completed flag. I have a page that create a task by setting a due date. Now I am going to create a background service in ASP.Net application itself that set IsCompleted to true for tasks when it reaches the due date.
See the Task class below.
public class Task { public int Id { get; set; } public string Title { get; set; } public DateTime DueDate { get; set; } public bool IsCompleted { get; set; } public DateTime ActionDate { get; set; } }
I created a CreateTask Page, that create a task by entering a title and date with time.
Let me add 10 tasks each varies its due date by 5 minutes. By default, the IsCompleted property is set to false and Action Date is null. Both these fields will be updated by the background service.
Background Service
A Background service can be created by inheriting your class from BackgroundService class
public class MyBackgroundService : BackgroundService
You need to implement the ExecuteAsync method and write your code. Before diving deep into the ExecuteAsync method, let us discuss about accessing scoped services from the background service. For e.g. you may need to access the ApplicationDbContext. For you to use scoped services, you need to create the scope. Let me show you how you can do that.
First you create a read-only property for the Interface IServiceScopeFactory in your background service class
public readonly IServiceScopeFactory ScopeFactory;
use Dependency Injection to get the scope factory instance in the constructor.
public MyBackgroundService(IServiceScopeFactory scopeFactory)
{
ScopeFactory = scopeFactory;
}
Now whenever you need a scoped service, just get it as below.
using (var scope = ScopeFactory.CreateScope())
{
var service = scope.ServiceProvider.GetRequiredService<ScopedService>();
}
For example, the following code will get the ApplicationDbContext
using (var scope = ScopeFactory.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
}
Once you have the services, you can use them in the ExecuteAsync method. Find the ExecuteAsync method that was written to update the Task status.
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
using (var scope = ScopeFactory.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
foreach(var pendingTask in db.Tasks.Where(t=>!t.IsCompleted && t.DueDate<DateTime.Now))
{
pendingTask.ActionDate = DateTime.Now;
pendingTask.IsCompleted = true;
}
db.SaveChanges();
}
await Task.Delay(TimeSpan.FromMinutes(5));
}
}
The ExecutiveAsync method will be running indefinitely by using a loop. The Task.Delay is implemented to release the thread and the code will be resumed after the delay minutes.
The above code is simple, when the code executes, it fetches all tasks from the database and update its IsCompleted property to true, if it’s duedate is passed.
Once you are done with the background service, you need to register it. In the Program.cs (.Net 6) or in startup.cs (in prior versions) you can register it with the AddHostedService method.
builder.Services.AddHostedService<MyBackgroundService>();
Now let us run the application and wait for some time.
You may observe that every 5 minutes difference, the tasks are getting updated. The reason is that I used Delay with 5 minutes, you may use your own logic here in ExecuteAsync and easily integrate background services to your ASP.Net applications.
Summary
Though the above example is too simple, it demonstrates the basic stuffs. You may have your own application logic where you can configure properties such as delays. Also you can have multiple types of synchronization, for e.g. you may have user expiry notifications once in a day and you may work with workflow tasks activity updates every 5 minutes.
Happy Programming!
References: