post-thumb

The easiest way to run background task in ASP .Net Core

It is common situation that we would like to run a background tasks in our application. There are many ways to do it, but we will focus on the easiest one.

Problem to solve

The best option is to show a real life scenario. I have a web application and that allows a user to upload a file. After uploading the file I need to do two things immediately: create a unique FileId and store the file somewhere. After it, I need to process the file, but it can take a bit of time, so I don’t want to block the client. I can prepare the response for the client with the FileId, maybe a status, and start processing it in the background.

Some ideas

Ok. So we know, that I need to run a background task. What options do we have?

  • In old ASP.NET I was able to use QueueBackgroundWorkItem, but in ASP.NET Core it is not possible.
  • I can use IHostedService or BackgroundService, but to use it I needed to write a bit of code to handle it. I would like to use something simpler.
  • I can use an external service like Azure Functions, but it is an overkill for my simple scenario.
  • I can use an library to handle it. After I bit of investigation I have found:
    • Hangfire
      • It is simple to use.
      • It is simple to configure.
      • It has build in dashboard.
      • It has a great documentation.
      • Last change in GitHub was yesterday.
    • Quartz.NET - It is also nice, but a bit more complicated.
      • It requires a bit more configuration.
      • It requires underspending their glossary like Trigger, Job, Scheduler.
      • It also has fantastic documentation.
      • Last change in GitHub was 3 days ago.

Hangfire configuration

Info

The full example you can find in the CodePruner.com repository

The 1st think you have to do it to install the Hangfire package.

Install-Package Hangfire.AspNetCore
Install-Package Hangfire.MemoryStorage

Then you have to configure the Hangfire service in the Program.cs file.


builder.Services.AddHangfire(configuration => configuration
    .UseRecommendedSerializerSettings()
    .UseMemoryStorage());

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


app.UseHangfireDashboard();

and you can also add Hangfire Dashboard. It is not required, but it is nice to have it.


app.UseHangfireDashboard();

and… it is everything to configure Hangfire.

Use Hangfire to run a background task

Now we can switch to the nicer part. We can create a job that will be run in the background. It can look like that:


app.MapGet("/ProcessLargeFileWithoutHangfire", async ([FromServices] SlowFileProcessor fileProcessor) =>
    {
        Console.WriteLine("[{0:HH:mm:ss}] Request start without Hangfire", DateTime.Now);
        var fileId = Random.Shared.Next(1, 1000);
        await new SlowFileProcessor().ProcessFileAsync(fileId);
        Console.WriteLine("[{0:HH:mm:ss}] Request end without Hangfire", DateTime.Now);
        return new FileProcessResult(fileId);
    })
    .WithName("ProcessLargeFileWithoutHangfire")
    .WithOpenApi();

app.MapGet("/ProcessLargeFileWithHangFire", ([FromServices] SlowFileProcessor fileProcessor) =>
    {
        Console.WriteLine("[{0:HH:mm:ss}] Request start with Hangfire", DateTime.Now);
        var fileId = Random.Shared.Next(1, 1000);
        BackgroundJob.Enqueue(() => fileProcessor.ProcessFileAsync(fileId));
        Console.WriteLine("[{0:HH:mm:ss}] Request end with Hangfire", DateTime.Now);
        return new FileProcessResult(fileId);
    })
    .WithName("ProcessLargeFileWithHangFire")
    .WithOpenApi();

as you can see, there are 2 endpoints ProcessLargeFileWithoutHangfire and ProcessLargeFileWithHangFire. I have also added a logging to see the difference.

When you do a request ProcessLargeFileWithoutHangfire then you can see result like.

[12:20:27] Request start without Hangfire
[12:20:27] Processing file 249...
[12:20:37] Processed file 249
[12:20:37] Request end without Hangfire

but when you do a request ProcessLargeFileWithHangFire then you can see result like.

[12:21:36] Request start with Hangfire
[12:21:36] Request end with Hangfire
[12:21:36] Processing file 153...
[12:21:46] Processed file 153

As you can see the most important difference is that the request with Hangfire ends immediately, but the processing is done in the background. It is exactly what we wanted to achieve. Now we could add a signalR, but it is not the subject of this post. If you would like to read about it, let me know in the comments section below.notification for example

More over, you can monitor these tasks in the Hangfire dashboard. You can see the status of the tasks, you can retry them, you can see the history of the tasks. It is really nice. and can look like: Example Hangfire dashboard

More advanced options

Hangfire has much more advanded capabilieties likse:

  • Delayed execution
  • Recurring tasks
  • Storage options
  • Automatic Retries
  • Possibility to run tasks on different servers

… but it is a story for a different post.

Summary

Thank you for reading this article. Would you like to add or ask anything? Let me know in the comments below.

comments powered by Disqus

Are you still here? Subscribe for more content!