Building a .NET Core 8 API with Hangfire and SQL Server

Imran KhanImran Khan
5 min read

In this tutorial, we'll guide you through creating a .NET Core 8 API project that leverages Hangfire for background processing and uses SQL Server for job storage. Hangfire is a powerful library that allows you to run background jobs, schedule tasks, and handle delayed or recurring tasks with ease. It's particularly useful for use cases such as:

  • Sending Emails: Automatically sending confirmation, notification, or reminder emails in the background without delaying the main thread.

  • Data Processing: Running intensive data processing tasks asynchronously, such as generating reports, processing files, or handling complex calculations.

  • Cleanup Tasks: Scheduling regular cleanup jobs to remove outdated or unnecessary data from your database or storage.

  • Notification Systems: Triggering push notifications, SMS, or other alerts at specified times or based on specific conditions.

  • Maintenance Jobs: Automating routine maintenance tasks like database backups, log rotation, or system health checks.

You can find the complete source code for this project on GitHub. By the end of this tutorial, you’ll have a robust API capable of handling these types of background tasks, along with a Hangfire dashboard to monitor job execution.

Prerequisites

Before we get started, make sure you have the following installed:

Step 1: Setting Up the Project

Begin by creating a new .NET Core 8 Web API project:

dotnet new webapi -n HangfireExample
cd HangfireExample

Step 2: Adding Hangfire to the Project

Hangfire is a library that simplifies background job processing in .NET applications. To add Hangfire to your project, install the necessary NuGet packages:

dotnet add package Hangfire
dotnet add package Hangfire.SqlServer

Next, configure Hangfire in your Program.cs file:

using Hangfire;
using Hangfire.SqlServer;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();

// Add Hangfire services.
builder.Services.AddHangfire(configuration => configuration
    .SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
    .UseSimpleAssemblyNameTypeSerializer()
    .UseRecommendedSerializerSettings()
    .UseSqlServerStorage(builder.Configuration.GetConnectionString("HangfireConnection"), new SqlServerStorageOptions
    {
        CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
        SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
        QueuePollInterval = TimeSpan.Zero,
        UseRecommendedIsolationLevel = true,
        UsePageLocksOnDequeue = true,
        DisableGlobalLocks = true
    }));

// Add the processing server as IHostedService
builder.Services.AddHangfireServer();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

// Use Hangfire dashboard
app.UseHangfireDashboard();

app.UseRouting();

app.UseAuthorization();

app.MapControllers();

app.Run();

Step 3: Adding a JobController

To demonstrate Hangfire in action, let's create a simple controller that enqueues a background job:

using Hangfire;
using Microsoft.AspNetCore.Mvc;
using System;

namespace HangfireExample.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class JobController : ControllerBase
    {
        private readonly IBackgroundJobClient _backgroundJobClient;

        public JobController(IBackgroundJobClient backgroundJobClient)
        {
            _backgroundJobClient = backgroundJobClient;
        }

        [HttpPost("enqueue")]
        public IActionResult EnqueueJob()
        {
            _backgroundJobClient.Enqueue(() => Console.WriteLine("Background job executed in .NET Core 8!"));
            return Ok("Job has been enqueued.");
        }

        [HttpPost("schedule")]
        public IActionResult ScheduleJob()
        {
            _backgroundJobClient.Schedule(() => Console.WriteLine("Scheduled job executed in .NET Core 8!"), TimeSpan.FromMinutes(1));
            return Ok("Job has been scheduled.");
        }
    }
}

Step 4: Configuring SQL Server with Docker or a Local Instance

To persist Hangfire jobs, we need a SQL Server database. You have two options here: set up SQL Server using Docker or connect to an existing local SQL Server instance.

Option 1: Using Docker

If you want to use Docker to run SQL Server, create a docker-compose.yml file:

version: '3.8'

services:
  sqlserver:
    image: mcr.microsoft.com/mssql/server:latest
    container_name: sqlserver
    environment:
      - SA_PASSWORD=YourStrong!Passw0rd
      - ACCEPT_EULA=Y
    ports:
      - "1433:1433"
    volumes:
      - sqlserverdata:/var/opt/mssql
    networks:
      - sqlnetwork

volumes:
  sqlserverdata:

networks:
  sqlnetwork:

Run the Docker container:

docker-compose up -d

Option 2: Using a Local SQL Server Instance

If you already have SQL Server installed on your local machine, you can skip the Docker setup. Simply ensure that your SQL Server instance is running and accessible.

Connecting to the SQL Server Database (For both option)

The following steps apply whether you're using Docker or a local SQL Server instance:

  1. Open SQL Server Management Studio (SSMS) or any other SQL management tool.

  2. Create a new database named Hangfire.

  3. Visualize the Database and Tables:

    • Once Hangfire is configured and your application runs for the first time, Hangfire will automatically create the necessary tables in the Hangfire database.

    • The tables will include ones for job storage, processing states, and more. They will look something like this:

Step 5: Connecting to SQL Server

Add your connection string in appsettings.json:

{
  "ConnectionStrings": {
    "HangfireConnection": "Server=localhost,1433;Database=Hangfire;User Id=sa;Password=YourStrong!Passw0rd;TrustServerCertificate=True;"
  }
}

Step 6: Configuring launchSettings.json

In your .NET Core 8 project, navigate to the Properties folder, and locate the launchSettings.json file. If it doesn’t exist, you can create it.

Here’s a typical launchSettings.json configuration for the project:

{
  "profiles": {
    "HangfireExample": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": false,
      "applicationUrl": "https://localhost:7002;http://localhost:7001",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": false,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "use64Bit": true,
      "sslPort": 44319
    }
  }
}

Step 7: Running and Testing the Application

Run your application:

dotnet run

Once the application is running, you can enqueue and schedule background jobs by sending POST requests to your API. Here’s how you can test these endpoints using an HTTP client:

Additionally, you can monitor and manage your background jobs using the Hangfire Dashboard, which is accessible at http://localhost:7002/hangfire.

Step 8: Using an HTTP Client to Test Endpoints

You can create an .http file to easily test the API endpoints. Alternatively, you can use postman client too. Here’s an example HangfireExample.http file:

@HangfireExample_HostAddress = http://localhost:7002

# Enqueue a Background Job
POST {{HangfireExample_HostAddress}}/api/job/enqueue
Content-Type: application/json

###

# Schedule a Background Job
POST {{HangfireExample_HostAddress}}/api/job/schedule
Content-Type: application/json

###

# Get Hangfire Dashboard (if exposed for testing)
GET {{HangfireExample_HostAddress}}/hangfire
Accept: text/html

You can use this .http file in a REST client like Visual Studio Code's REST Client extension to quickly send requests to your API.

Conclusion

In this tutorial, we built a .NET Core 8 API that integrates Hangfire for handling background jobs. We also set up SQL Server using Docker to persist these jobs and used the Hangfire Dashboard to monitor and manage them.

This setup is highly versatile and can be extended to meet your specific requirements, whether it's adding more complex background jobs or deploying your API in a production environment. Happy coding!

0
Subscribe to my newsletter

Read articles from Imran Khan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Imran Khan
Imran Khan

As a Senior Software Engineer with over five years of experience in developing enterprise applications and SaaS solutions, I specialize in full-stack development with a strong foundation in ASP.NET Core, SQL, ReactJS, and Node.js. My expertise lies in designing scalable, high-performance systems, managing complex databases, and implementing microservices. I've successfully delivered projects across diverse domains, from financial applications to human resource systems, ensuring quality and efficiency through agile practices. I thrive in collaborative environments, continuously striving to enhance software performance and drive innovation.