Unofficial guide/questions for setting up a .NET 6 (Core) Web app with Hangfire

This is based only on my own brief testing, and is not intended to be an official guide.

I’m using Visual Studio 2022, which installs .NET 6, and I used the “ASP.NET Core Web App” project type/template. I am more familiar with “Web Forms (.NET Framework v4.8) ASP.NET Web” applications, and I am very new to .NET Core applications and even more so to .NET 6. Please reply with any corrections or changes that might be needed or recommended.

Also, this information is only intended to help with the initial setup of Hangfire in an “ASP.NET Core Web App” project, not with any of the code for adding or managing scheduled or recurring tasks.

I started with the documentation on this page for getting started with Hangfire in ASP.NET Core Applications:
ASP.NET Core Applications — Hangfire Documentation
Since this documentation is based on older versions of ASP.NET Core, I also then used this page on migrating [from ASP.NET Core v5] “to the new minimal hosting model in ASP.NET Core 6.0”:
Code samples migrated to the new minimal hosting model in 6.0 | Microsoft Docs

Note: You may need to disable the “Hot reload” features in the “ASP.NET Core” options in Visual Studio 2022 to allow the Hangfire page to load completely - see this discussion topic:
Dashboard page blank after upgrade to .net 6.0
I needed to disable these options to initially have the project page load completely, but later I was able to re-enable these features and the page continued working correctly.

In Visual Studio 2022, I created a new project and selected the following project template:
ASP.NET Core Web App: A project template for creating an ASP.NET Core application with example ASP.NET Razor Pages content.”
I named the new project, and then selected “.NET 6.0 (Long-term support)” for the framework, and “Configure for HTTPS”, and created the new application. The project should run at this point, and display a simple home page.

Next, run the NuGet Package Manager (right-click on the project name and select “Manage NuGet Packages…”); click the “Browse” tab. Search for, and install, each of the following 3 packages:

Hangfire.AspNetCore
Hangfire.Core
Hangfire.SqlServer

NOTE: I’m connecting to a SQL Server database for Hangfire, so I need the “Hangfire.SqlServer” package; you may or may not need this package.

Create a new empty SQL Server database (if that’s what you’re using for storage) - I created a database named “HangfireTest” in the “LocalDB” instance, which is “(localdb)\MSSQLLocalDB”.

Open the files “appsettings.json” and “appsettings.Development.json” and add the following to the beginning of both files (immediately after the opening “{”):

“ConnectionStrings”: {
“HangfireConnection”: “Server=(localdb)\MSSQLLocalDB;Database=HangfireTest;Integrated Security=SSPI;”
},

Change the connection string as appropriate for your database.

Open the “Program.cs” file; note that there is NO Startup.cs file in this minimal style of .NET 6 Core web application.

At the very beginning of Program.cs, add these 2 lines:

using Hangfire;
using Hangfire.SqlServer;

Immediately AFTER the line “var builder = WebApplication.CreateBuilder(args);” in Program.cs, add the following:

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,
UseRecommendedIsolationLevel = true,
DisableGlobalLocks = true
}));
builder.Services.AddHangfireServer();

I don’t believe that you need to add “builder.Services.AddMvc();” in this project type, since it is using Razor pages and already contains the following: “builder.Services.AddRazorPages();”

Immediately AFTER the line “app.UseAuthorization();” in Program.cs, add:

app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHangfireDashboard();
});

After the line “app.Run();” in Program.cs, add:

BackgroundJob.Enqueue(() => Console.WriteLine(“Hello, world!”));
using (var server = new BackgroundJobServer())
{
Console.ReadLine();
}

Note: This is probably not the appropriate place to include code to submit jobs to the queue, but this is okay for a test.

Now, you should be able to build and run the application. You may need to accept a self-signed server certificate if you haven’t previously run a .NET 6 project. After that, the tables should be created in the database, and the default/home page for the new app should be displayed in a browser. Once the home page is displayed, you can add “/hangfire” to the end of your local URL, and you should then see the Hangfire dashboard.

You may also want to change the “run” option in Visual Studio (next to the green “run” icon) from the project name to “IIS Express” (so that it doesn’t run a console window).

Does anyone have additional suggestions for this, or are there any errors in the above?

Hi,
Thanks for taking the time to do this.
I followed your instructions and it got a hangfire server working… but no job started…
I can see the console etc and the database got created… so thats a good start.
I’ll press on and see if I can get something working… I’m in the same boat tho… learning .net core 6.0 while also trying to get to grips with Hangfire.

Copy and pasting code from your initial post is hampered by those damnable ‘smart quotes’ so if anybody has problems maybe they should start by replacing quotation marks…
I’ll try post my version of the code here now and see if putting it in code blocks will avoid smart quotes.

My appsettings.development.json is as follows:

{
  "ConnectionStrings": {
    //"HangfireConnection": "Server=(localdb)\\MSSQLLocalDB;Database=HangfireTest;Integrated Security=SSPI;",
    "HangfireConnection": "Server=.\\SQLEXPRESS;Database=HangfireTest;Integrated Security=SSPI;"
  },
  "DetailedErrors": true,
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  }
}

You’ll note that I just used SQLExpress for my database.

Then the Program.cs file is as follows:

using Hangfire;
using Hangfire.SqlServer;



var builder = WebApplication.CreateBuilder(args);

//---------------------------------------

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,
    UseRecommendedIsolationLevel = true,
    DisableGlobalLocks = true
}));
builder.Services.AddHangfireServer();

//----------------------------------------

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

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
    endpoints.MapHangfireDashboard();
});

app.MapRazorPages();

app.Run();

BackgroundJob.Enqueue(() => Console.WriteLine("Hello, world!"));
using (var server = new BackgroundJobServer())
{

    Console.ReadLine();
}

From what I can see tho… nothing after app.Run(); gets executed… possibly I’m wrong but I’m running this is IISExpress…

Anyway… if we can keep the .net core 6.0 experiments going maybe we can contribute something in a few days.

Thanks for letting me know this was useful. Sorry about the quotes; I should have marked the sections as code.

Also, my mistake about where the example “BackgroundJob.Enqueue” and related statements should go - I believe these all need to be before the “app.Run();” statement (I think I realized this after posting my original entry, and forgot to go back and correct it). You can always add a breakpoint on this line to see if it is executed.

Hi @philipd ,
Don’t worry about the quotes :grinning: I just mentioned it in case it might help anyone wasn’t wise to them. You were great to get this started at all. :+1:

Yeah, I actually was able to add a page and put a button on it and call BackgroundJob.Enqueue in the OnPost method of the model and it worked! yayy…

Id still really like to be able to put a list of currently processing jobs on my simple page and then allow a user to send a cancel message to one of them, but I can’t figure out how the dashboard does it… possibly I’d read it from the database?
(Quartz.net has a method on the scheduler that you call to get back a list)

But still not sure (a) how to send a cancel and (b) what identifier to use to specidfy which job I want to stop…

But shur, this is the battle isn’t it? :sweat_smile: We just have to keep pluggin away and find our way to a solution…

I’ll put updates here as I get closer to a solution.
Thanks again.

I found related info in this post

http://hangfire.discourse.group/t/cancel-a-running-job/603/10

the very last message on that thread appears to have something close to what I need.

Interestingly they mention a Kill job facility in case a job gets stuck or doesn’t respond to the cancellation request… that would definitely be a thing I would be interested in too.

Ok, so I have now got a simple asp.net core 6.0 app up and running and I can add jobs in response to a button click and I can stop them in response to another button click.
I’ll share the code if anybody is interested.

I have one issue tho whenever I stop the debugger in VS … servers get left behind… so it looks like i need to shut down the background server properly… The docs are showing how to do this is an old .asax way here:

https://docs.hangfire.io/en/latest/background-processing/processing-jobs-in-web-app.html

So… I’m just wondering how I should do it in .net core 6 ??
I create the background server in program.cs

builder.Services.AddHangfireServer();

but not sure how or where to dispose of it.

To GoOnYahGoodThing

My understanding is that the servers will be automatically cleaned up (disposed), but not immediately. And that has been my experience in my brief testing. If I start and stop my test application a few times, I can end up with a few servers (as shown in the dashboard), but then after a little while they are each removed until only one remains running.

At the very end of the article you linked: Processing jobs in a web application — Hangfire Documentation
There is a short block of code where it states:
“This line creates a new instance of the BackgroundJobServer class automatically, calls the Start method and registers method Dispose invocation on application shutdown.”

Note that the page above shows calling app.UseHangfireServer(); but this method is now obsolete - if you add it to your code, a code tooltip will show:
“[…] UseHangfireServer is obsolete: use […].AddHangfireServer estension method instead.”

The current approach for .NET Core is the following:

builder.Services.AddHangfireServer();

This should be called just after defining the Hangfire configuration with builder.Services.AddHangfire([…]).

If you use the above line, I don’t believe that you need to do anything to dispose of the Hangfire servers when you shut down your application.

If you would like to post your code for your working app, I would appreciate that.

Hiya @philipd
I have put the VERY basic app up on github in this repo.

Its deliberately very unsophisticated… obviously it needs auto refreshing of lists etc… but I just wanted to keep the code easily understandable for now.

I will add code to do scheduled jobs when i get back to work…

@GoOnYahGoodThing

Thanks for posting your code. That should be helpful for others as well.

I will have a look tonight or tomorrow.

BTW you don’t happen to know if there is a way to limit a recurring job to a fixed number of repeats?
Like set up a job to recur at 8pm over the next, say, 4 nights?
Can’t seem to see it in the docs.

@GoOnYahGoodThing - I’m not aware of any way to do that. Perhaps as a workaround you could call “BackgroundJob.Schedule” 4 times with 4 different DateTimeOffset values.