How to use Hangfire in ASP.NET Core

This article demonstrates how to use HangFire, a popular open source library for background processing in .NET Core applications. This article builds a simple application that performs some background processing to demonstrate the concepts covered using PostgreSql as a database to store data and dotConnect for PostgreSQL as a data provider for PostgreSQL.

To store and retrieve data from the database, we will use dotConnect for PostgreSQL , a high-performance enhanced PostgreSQL data provider built on ADO.NET that can work in connected and disconnected modes.

You will need the following tools to work with the code examples: Qqun: 765665608 

dotConnect for PostgreSQL

What will we construct?

In this article, we will build a simple application that demonstrates how to customize HTTP 400 and 404 error responses in ASP.NET 6. Here are the steps we will follow in this article to accomplish this:

  1. Understanding Hangfire in ASP.NET Core
  2. Create an ASP.NET 6 Core Web API project in Visual Studio 2022
  3. Add the Devart.Data.PostgreSql NuGet package to the API project
  4. Create a simple WebAPI controller
  5. Create method inside controller to retrieve resource usage information and save it in database table
  6. Create fire-and-forget and repetitive tasks
  7. Run application
What are background tasks?

In today's applications, certain operations such as sending an email, creating a report, or uploading a large file can take a long time to complete. If your application performs these operations synchronously within the request-response cycle, timeouts and delays may result. This is where background tasks can help.

Background tasks are tasks that perform operations in the background so that the application's workflow is not interrupted and, most importantly, the currently executing thread is not blocked. In other words, background tasks allow you to separate time-consuming or non-critical operations from the main logic of your application.

Background tasks typically run asynchronously and are used to offload time-consuming or resource-intensive processes from the application's actual request response cycle, allowing them to run in the background. By decoupling background tasks from immediate user interaction, background tasks allow you to send emails, modify data, or queue alerts without requiring an immediate user response.

You can configure background jobs to trigger periodically. This is useful for automating normal processes such as backups, syncing, and generating daily reports. You can focus on core functionality and offload certain background tasks to other components, making your code more maintainable.

What is Hangfire?

Hangfire is an open source library for handling background tasks in .NET and ASP.NET Core applications. It provides a simple and effective way to perform long, time-consuming or repetitive tasks outside of the normal request-response cycle.

Hangfire simplifies the implementation of task background processing by providing a unified and easy-to-use API. It eliminates the need to write complex threading code or manually manage background processing infrastructure. Due to its intuitive API and rich functionality, Hangfire is popular among developers for managing auxiliary tasks in .NET applications.

Features of Hangfire

The following are the main features of Hangfire:

  • Background Jobs: Hangfire allows you to define jobs or activities that need to run in the background, independent of user requests. These activities may include operations that require significant central processing unit (CPU) processing power, such as generating reports, sending emails, or any other task that can benefit from asynchronous processing.
  • Persistent Storage: Hangfire uses persistent storage to store information about queued, scheduled, and processed tasks. This prevents tasks from being lost if the application restarts or fails. Hangfire supports a variety of storage options, including SQL Server, PostgreSQL, and Redis.
  • Job Scheduling: Hangfire makes it easy to schedule tasks at specific times or recurring intervals. You can schedule tasks to run once or repeatedly, or define complex schedules using cron-like expressions.
  • Dashboard: Hangfire provides a web-based interface that allows you to monitor and manage auxiliary tasks. The dashboard provides visibility into task status, execution history, processing statistics, and the ability to manually start or terminate jobs.
  • Fault Tolerance: Hangfire ensures fault tolerance by automatically retrying failed tasks. If a job fails due to an exception, Hangfire can be configured to retry the job a specified number of times with a certain amount of time between retries.
  • Distributed processing: Hangfire facilitates distributed processing by allowing applications to scale across multiple servers or worker processes. This enables parallel processing of tasks and improves overall system efficiency.
Components of Hangfire

Hangfire uses three main components to manage jobs. These include storage, clients, and servers. The storage component is responsible for storing job information. The client component is used to create background jobs and store the jobs in the data store based on the configured storage provider. The server component is responsible for performing all background jobs.

Create a new ASP.NET 6 Core Web API project

In this section, we will learn how to create a new ASP.NET 6 Core Web API project in Visual Studio 2022.

Now, follow these steps:

  1. Open Visual Studio 2022.
  2. Click Create New Project .
  3. Select ASP.NET Core Web API and click Next.
  4. Specify the project name and where the project is stored on the system. Alternatively, select the " Place solution and project in the same directory" checkbox.
  5. Click Next.
  6. In the "Additional Information" window, select ".NET 6.0 (Long Term Support)" as the project version.
  7. Disable the "Configure HTTPS" and "Enable Docker support" options (uncheck them).
  8. Since we won't be using authentication in this example, select the Authentication Type as None .
  9. Since we won't be using Open API in this example, uncheck the Enable OpenAPI support checkbox.
  10. Since we won't be using the minimal API in this example, make sure Use Controller is checked (uncheck to use the minimal API) .
  11. Uncheck the "Don't use top-level statements" checkbox.
  12. Click Create to complete the process.

We will use this project in this article.

Implement automatic resource monitor

In this example, we'll look at how to implement an automatic resource monitor using Hangfire in ASP.NET Core. The application creates and runs recurring tasks in the background at predefined intervals, then retrieves and stores CPU and memory usage information in a PostgreSQL database table.

Create database

You can use the pgadmin tool to create the database. To start creating a database using this tool, follow these steps:

  1. Start the pgadmin tool
  2. Expand the server section
  3. Select database
  4. Right click and click Create -> Database...
  5. Specify the database name and leave other options at their default values
  6. Click Save to complete the process

Now follow the steps given below to create a table in the database you just created:

  1. Select and expand the database you just created
  2. Select Schema->Table
  3. Right click on "Table" and select "Create" -> "Table..."

The table script is given below for your reference:

CREATE TABLE perfdata (
id serial PRIMARY KEY,
job_Id VARCHAR ( 255 ) NOT NULL,
cpu_usage VARCHAR ( 255 ) NOT NULL,
memory_usage VARCHAR ( 255 ) NOT NULL
);

We will use this table later in this article to demonstrate how to use dotConnect for PostgreSQL for integration testing in ASP.NET Core.

Install the NuGet package into the API project

Next, you should install the required NuGet packages into your project. You can install them from the NuGet Package Manager tool within Visual Studio or from the NuGet Package Manager console using the following command:

PM> Install-Package Devart.Data.PostgreSql
PM> Install-Package Hangfire
PM> Install-Package Hangfire.MemoryStorage

dotConnect for PostgreSQL  is a high-performance data provider for PostgreSQL built on ADO.NET technology, providing a comprehensive solution for building database applications based on PostgreSQL.

Configure Hangfire in ASP.NET Core

After installing Hangfire into your project, the next step is to configure it. You can add the Hangfire service to the services collection using the following code:

builder.Services.AddHangfire(c => c.UseMemoryStorage());
builder.Services.AddHangfireServer();

You can view real-time data related to running or completed jobs in the dashboard. You can enable this feature with the code snippet given below:

app.UseHangfireDashboard();
Create a job in Hangfire

In this section, we'll look at how to create a job in Hangfire to perform processing in the background while our application continues to respond.

Hangfire provides support for the following job types:

  • Fire-and-forget jobs: These jobs are executed only once
  • Delayed jobs: These jobs are executed after a specific time interval has elapsed
  • Continuous jobs: These jobs are executed immediately after the execution of the parent job completes
  • Periodic jobs: These jobs are executed periodically according to a schedule
Create once-and-forget jobs

You can create background jobs in Hangfire using the BackgroundJob.Enqueue method. Fire-and-forget jobs are jobs that are triggered or executed only once. Create a new API controller named CustomJobController and write the following code in it:

[Route("api/[controller]")]
[ApiController]
public class CustomJobController : ControllerBase
{
private readonly ILogger _logger;
private readonly IBackgroundJobClient _backgroundJobClient;
public CustomJobController(ILogger<CustomJobController> logger, IBackgroundJobClient backgroundJobClient)
{
_log = log;
_backgroundJobClient = backgroundJobClient;
}

[HttpGet]
public IActionResult Get()
{
var jobId = _backgroundJobClient.Enqueue(() => FireAndForgetJob(null));
Thread.Sleep(5000);
return Ok($"Job Id: {jobId} completed...");
}

public Task FireAndForgetJob(PerformContext context)
{
var jobId = context.BackgroundJob.Id;
_logger.LogInformation($"Executing Job Id: {jobId}...");
return Task.CompletedTask;
}
}

When you execute the application and hit the CustomJobController's HttpGet endpoint, you can see the fire-and-forget job execute once, as shown in Figure 1:

Create a delayed job

The following code snippet shows how to create a deferred job in Hangfire:

var jobId = BackgroundJob.Schedule(() => Console.WriteLine("This is an example of a delayed job"), TimeSpan.FromDays(1));
Create a continuation job

In Hangfire, a continuous job refers to a job that runs immediately after the execution of the parent job completes. You can also create and manage continuation tasks or jobs in Hangfire. The following code snippet shows how to create a continuation task in Hangfire:

var id = BackgroundJob.Enqueue(() => InitializeInputData());
BackgroundJob.ContinueWith(id, () => ValidateInputData());
Create a recurring job

Recurring jobs are jobs that are performed according to a schedule. For example, you could have a recurring job that executes every minute, every hour, or every hour of the week. To handle recurring jobs in Hangfire, you should use the IRecurringJobManager interface. Actually, you should inject an instance of IRecurringJobManager interface type in the constructor of CustomJobController class as shown in the code listing given below:

[Route("api/[controller]")]
[ApiController]
public class CustomJobController : ControllerBase
{
private readonly ILogger _logger;
private readonly IRecurringJobManager _recurringJobManager;
public CustomJobController(ILogger<CustomJobController> logger,
IRecurringJobManager recurringJobManager)
{
_log = log;
_recurringJobManager = recurringJobManager;
}

[HttpGet]
public IActionResult Get()
{
var jobId = Guid.NewGuid().ToString();
_recurringJobManager.AddOrUpdate(jobId, () => CustomRecurringJob(jobId),
Cron.Minutely);
Thread.Sleep(5000);
return Ok($"Job Id: {jobId} completed...");
}

public Task CustomRecurringJob(string jobId)
{
_logger.LogInformation($"Executing Job Id: {jobId} at {DateTime.Now}");
return Task.CompletedTask;
}
}

When you execute the application and access the HttpGet endpoint, you can see the job executing every minute, as shown in Figure 2:

Retrieve resource consumption information

Now let's create a recurring job in Hangfire and use it to retrieve CPU and memory usage data. How the following code is supposed to get resource usage information on your computer:

private dynamic GetResourceUsageForProcess()
{
string currentProcessName = Process.GetCurrentProcess().ProcessName;
PerformanceCounter cpuCounter = new PerformanceCounter("Process",
"% Processor Time", currentProcessName, true);
PerformanceCounter memoryCounter = new PerformanceCounter("Process",
"Private Bytes", currentProcessName, true);
cpuCounter.NextValue();
memoryCounter.NextValue();
Task.Delay(500);
dynamic result = new ExpandoObject();
result.CPU = Math.Round(cpuCounter.NextValue() /
Environment.ProcessorCount, 2);
result.RAM = Math.Round(memoryCounter.NextValue() / 1024 / 1024, 2);
return result;
}
Store resource consumption information in the database

Now that the resource usage information is available, you should store this information in the database.

The StoreResourceUsageData method given below shows how to save CPU and memory usage data in a PostgreSql database.

public void StoreResourceUsageData(string job_id, string cpu_usage, string
memory_usage)
{
try
{
using (
PgSqlConnection pgSqlConnection = new PgSqlConnection
("User Id = postgres; Password = sa123#;" +
"host=localhost;database=demo; license key=Specify your license key here;"))
{
using (PgSqlCommand cmd = new PgSqlCommand())
{
cmd.CommandText = "INSERT INTO perfdata (job_id, cpu_usage, memory_usage) "
+ "VALUES (:job_id, :cpu_usage, :memory_usage)";

Thread.Sleep(250);
cmd.Connection = pgSqlConnection;
cmd.Parameters.AddWithValue("job_id", job_id);
cmd.Parameters.AddWithValue("cpu_usage", cpu_usage);
cmd.Parameters.AddWithValue("memory_usage", memory_usage);

if (pgSqlConnection.State != System.Data.ConnectionState.Open)
pgSqlConnection.Open();

var state = pgSqlConnection.State;
cmd.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
throw;
}
}
Complete source code

The complete source code of CustomJobController is given below for reference:

[Route("api/[controller]")]
[ApiController]
public class CustomJobController: ControllerBase
{
private readonly ILogger _logger;
private readonly IRecurringJobManager _recurringJobManager;
protected static PerformanceCounter cpuCounter;
protected static PerformanceCounter memoryCounter;
public CustomJobController(ILogger < CustomJobController > logger,
IRecurringJobManager recurringJobManager)
{
_log = log;
_recurringJobManager = recurringJobManager;
}
[HttpGet]
public IActionResult Get()
{
var jobId = Guid.NewGuid().ToString();
_recurringJobManager.AddOrUpdate(jobId, () => CustomRecurringJob(jobId),
Cron.Minutely);
Thread.Sleep(5000);
return Ok($"Job Id: {jobId} completed...");
}
public Task CustomRecurringJob(string job_id)
{
_logger.LogInformation($"Executing Job Id: {job_id} at {DateTime.Now}");
var resourceUsage = GetResourceUsageForProcess();
string cpu_usage = resourceUsage.CPU.ToString();
string memory_usage = resourceUsage.RAM.ToString();
StoreResourceUsageData(job_id, cpu_usage, memory_usage);
return Task.CompletedTask;
}
private dynamic GetResourceUsageForProcess()
{
string currentProcessName = Process.GetCurrentProcess().ProcessName;
PerformanceCounter cpuCounter = new PerformanceCounter
("Process", "% Processor Time",
currentProcessName, true);
PerformanceCounter memoryCounter = new PerformanceCounter
("Process", "Private Bytes",
currentProcessName, true);
cpuCounter.NextValue();
memoryCounter.NextValue();
Task.Delay(500);
dynamic result = new ExpandoObject();
result.CPU = Math.Round(cpuCounter.NextValue() /
Environment.ProcessorCount, 2);
result.RAM = Math.Round(memoryCounter.NextValue() / 1024 / 1024, 2);
return result;
}
public void StoreResourceUsageData(string job_id, string cpu_usage,
string memory_usage)
{
try
{
using(PgSqlConnection pgSqlConnection = new PgSqlConnection
("User Id = postgres; Password = sa123#;" +
"host=localhost;database=demo; license key=Your license key;"))
{
using(PgSqlCommand cmd = new PgSqlCommand())
{
cmd.CommandText = "INSERT INTO perfdata “ +
“(job_id, cpu_usage, memory_usage) " +
"VALUES (:job_id, :cpu_usage, :memory_usage)";
Thread.Sleep(250);
cmd.Connection = pgSqlConnection;
cmd.Parameters.AddWithValue("job_id", job_id);
cmd.Parameters.AddWithValue("cpu_usage", cpu_usage);
cmd.Parameters.AddWithValue("memory_usage", memory_usage);
if (pgSqlConnection.State != System.Data.ConnectionState.Open)
pgSqlConnection.Open();
var state = pgSqlConnection.State;
cmd.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
throw;
}
}
}

When you run the application, you can see the CPU and memory usage information stored in the database table we created earlier.

Hangfire is an excellent library for integrating jobs into .NET and .NET Core applications. You can use it to schedule your jobs at specific times, periodically, or based on cron expressions. By using Hangfire, you can improve the responsiveness of your application. With Hangfire and ASP.NET Core, you can complete time-consuming tasks efficiently in the background, maximizing your application's performance and user experience.

Guess you like

Origin blog.csdn.net/m0_67129275/article/details/132898617