Integrating Hangfire into an ASP.NET Core Web API allows you to effortlessly manage and execute background tasks, enhancing application responsiveness and scalability by offloading long-running operations from the main request thread.
Hangfire provides a robust solution for processing various types of background jobs, including fire-and-forget, delayed, recurring, and continuations, all while offering a powerful dashboard for monitoring. Here's a comprehensive guide to setting it up and using it within your Web API project.
1. Project Setup: Creating an ASP.NET Core Web API Project
Begin by creating a new ASP.NET Core Web API project. This serves as the foundation for integrating Hangfire. You can do this using the .NET CLI or Visual Studio.
Using .NET CLI:
dotnet new webapi -n MyHangfireApi
cd MyHangfireApi
2. Package Installation: Integrating Hangfire NuGet Packages
Next, install the necessary Hangfire NuGet packages. You'll typically need Hangfire.Core
and a specific storage provider package, such as Hangfire.SqlServer
for SQL Server.
Using .NET CLI:
dotnet add package Hangfire.Core
dotnet add package Hangfire.SqlServer
Using Package Manager Console (Visual Studio):
Install-Package Hangfire.Core
Install-Package Hangfire.SqlServer
3. Database Configuration: Setting Up a Storage Backend for Hangfire
Hangfire requires a persistent storage mechanism to store job details, states, and runtime information. While the Hangfire packages don't create the database itself, you'll need to prepare one. Common storage options include:
- SQL Server: Recommended for most production environments due to its robustness.
- PostgreSQL: Another popular relational database option.
- Redis: Excellent for high-performance, short-lived jobs where an in-memory or key-value store is sufficient.
For SQL Server, ensure you have an existing database or create a new one where Hangfire can create its tables.
4. Connection String Management: Adding to appsettings.json
To connect Hangfire to your chosen database, you need to provide a connection string. Add this connection string to your appsettings.json
file.
Example appsettings.json
:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"HangfireConnection": "Server=(localdb)\\mssqllocaldb;Database=HangfireDB;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
Replace HangfireDB
and the server details with your actual database name and server instance.
5. Service Integration: Configuring Hangfire in Program.cs
The final step for setup involves configuring Hangfire services and its server in your Program.cs
file (or Startup.cs
in older ASP.NET Core versions). This includes adding Hangfire to the service collection and enabling its dashboard.
using Hangfire;
using Hangfire.SqlServer;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Configure Hangfire services
builder.Services.AddHangfire(configuration => configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseSqlServerStorage(builder.Configuration.GetConnectionString("HangfireConnection"), new SqlServerStorageOptions
{
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
QueuePollInterval = TimeSpan.Zero,
Use((IBackgroundJobFactory)serviceProvider.GetRequiredService<IBackgroundJobFactory>()), // Deprecated, remove or use a newer approach for DI
DisableGlobalLocks = true // Only use in specific scenarios
}));
// Add the Hangfire server (responsible for processing jobs)
builder.Services.AddHangfireServer();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
// Enable the Hangfire Dashboard
app.UseHangfireDashboard(); // Access at /hangfire
app.MapControllers();
app.Run();
Key Configuration Points:
AddHangfire(...)
: Registers Hangfire's core services. Inside this,UseSqlServerStorage
specifies your chosen database and passes the connection string.AddHangfireServer()
: Activates the Hangfire server process, which continuously monitors the database for new jobs and executes them. Without this, jobs would be enqueued but never processed.UseHangfireDashboard()
: Enables the Hangfire Dashboard, typically accessible at/hangfire
(e.g.,https://localhost:xxxx/hangfire
). This dashboard is invaluable for monitoring job statuses, managing recurring tasks, and troubleshooting. For production environments, ensure you secure the dashboard with proper authorization.
6. Job Creation and Enqueueing: Defining and Scheduling Background Tasks
Now that Hangfire is configured, you can start creating and enqueuing background jobs within your Web API controllers or services. Hangfire supports several job types:
Types of Hangfire Jobs
-
Fire-and-Forget Jobs: Executed only once and almost immediately after creation. Ideal for tasks like sending emails or notifications.
BackgroundJob.Enqueue(() => Console.WriteLine("Hello, Hangfire!"));
-
Delayed Jobs: Executed once after a specified delay. Useful for scheduling tasks to run after a certain time, like a reminder.
BackgroundJob.Schedule(() => Console.WriteLine("Delayed message after 1 minute!"), TimeSpan.FromMinutes(1));
-
Recurring Jobs: Executed many times on a specified CRON schedule. Perfect for daily reports, data synchronization, or clean-up tasks.
RecurringJob.AddOrUpdate( "my-daily-report", () => Console.WriteLine("Generating daily report..."), Cron.Daily); // Runs once every day at midnight
-
Continuations: Executed when a parent job has finished. This allows for chaining multiple background tasks.
string parentJobId = BackgroundJob.Enqueue(() => Console.WriteLine("Parent job completed.")); BackgroundJob.ContinueWith(parentJobId, () => Console.WriteLine("Child job started after parent."));
Practical Example: Creating a Background Job to Call an API
A common use case in Web APIs is to offload long-running external API calls. Here's how you might define a simple background method and enqueue it from an API controller.
First, define a service or a static class containing the method to be executed in the background.
// Services/MyBackgroundService.cs
public class MyBackgroundService
{
private readonly ILogger<MyBackgroundService> _logger;
public MyBackgroundService(ILogger<MyBackgroundService> logger)
{
_logger = logger;
}
public async Task CallExternalApiAsync(string payload)
{
_logger.LogInformation($"[Hangfire Job] Starting API call with payload: {payload}");
// Simulate a long-running API call
await Task.Delay(5000);
_logger.LogInformation($"[Hangfire Job] API call completed for payload: {payload}");
// In a real scenario, this would involve HttpClient calls
}
public void ProcessSimpleTask(string message)
{
_logger.LogInformation($"[Hangfire Job] Processing simple task: {message}");
}
}
Register this service for Dependency Injection:
// In Program.cs, within AddServices()
builder.Services.AddScoped<MyBackgroundService>();
Now, create a controller to expose an endpoint that enqueues this background job.
// Controllers/BackgroundJobController.cs
using Hangfire;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("[controller]")]
public class BackgroundJobController : ControllerBase
{
private readonly IBackgroundJobClient _jobClient;
// Hangfire's IBackgroundJobClient is automatically resolved
public BackgroundJobController(IBackgroundJobClient jobClient)
{
_jobClient = jobClient;
}
/// <summary>
/// Enqueues a fire-and-forget job to call an external API.
/// </summary>
[HttpPost("fire-and-forget")]
public IActionResult EnqueueFireAndForgetApiCall([FromBody] string payload)
{
_jobClient.Enqueue<MyBackgroundService>(service => service.CallExternalApiAsync(payload));
return Ok($"Fire-and-forget job enqueued for payload: {payload}");
}
/// <summary>
/// Schedules a job to process a message after a delay.
/// </summary>
[HttpPost("delayed")]
public IActionResult EnqueueDelayedTask([FromBody] string message, [FromQuery] int delayMinutes = 1)
{
_jobClient.Schedule<MyBackgroundService>(service => service.ProcessSimpleTask(message), TimeSpan.FromMinutes(delayMinutes));
return Ok($"Delayed job scheduled for message: '{message}' in {delayMinutes} minutes.");
}
/// <summary>
/// Adds or updates a recurring job.
/// </summary>
[HttpPost("recurring")]
public IActionResult AddOrUpdateRecurringJob()
{
RecurringJob.AddOrUpdate<MyBackgroundService>(
"my-daily-cleanup", // Unique job ID
service => service.ProcessSimpleTask("Running daily cleanup."),
Cron.Daily); // Runs every day
return Ok("Daily cleanup recurring job added/updated.");
}
}
When you hit the fire-and-forget
endpoint, the API call will return immediately, and the CallExternalApiAsync
method will be executed in the background by the Hangfire server. This significantly improves the responsiveness of your Web API.
Best Practices and Considerations
- Error Handling: Implement robust error handling within your background job methods. Hangfire supports automatic retries for failed jobs, which can be configured.
- Dependency Injection: Jobs executed by Hangfire can leverage dependency injection just like your controllers. Simply inject services into the constructor of the class containing your background method (e.g.,
MyBackgroundService
). - Scalability: For high-volume applications, you can run multiple Hangfire servers, all connected to the same database. This allows for horizontal scaling of job processing.
- Dashboard Security: Always secure the Hangfire Dashboard in production. You can implement custom authorization filters to restrict access.
- Serialization: Be mindful of the data you pass to background jobs. Ensure complex objects are properly serializable or pass only necessary IDs/primitives.
By following these steps, you can effectively integrate and utilize Hangfire in your ASP.NET Core Web API to manage and process background tasks efficiently, leading to more responsive and scalable applications.