Contact Us

Async processing of long-running tasks in ASP.NET Core |

Mobile App | March 13, 2022

Async processing of long-running tasks in ASP.NET Core

Sometimes, invoking an API endpoint needs to trigger a long-running task. Examples of this could be invoking an external and slow API or sending an email, which you don’t want the caller of your API to wait for. There are multiple ways of implementing this using a message broker, a fire and forget API request, or something completely third. In this post, I’ll show you how to implement async processing in ASP.NET Core, using a queue and the Background Worker feature.

To understand how to process long-running tasks, let’s start by creating an example and making it sloooooow. Create a new ASP.NET Core application through Visual Studio, dotnet, or your preferred scaffolding engine. For this example, I have chosen the MVC template, but it could just as well be one of the other options.

Next, in the HomeController.cs file, create a new method to simulate a call to a slow running task:

For the demo, I’m waiting 10 seconds to simulate some work. The Task.Delay line would be replaced with some integration code in a real-life example. I have wrapped the code in information messages, which I can later inspect in Visual Studio or through the configured logger (maybe

Then, invoke the CallSlowApi method from the Index method:

Let’s run the application and inspect the performance in Developer Tools:

As expected, loading the frontpage takes just above 10 seconds (10 seconds for the Task.Delay and 30 milliseconds to load the page).

Refactoring time! To process the message asynchronously, we’ll implement a background worker in ASP.NET Core with an async queue in front. Let’s start with the queue. Add a new class named BackgroundWorkerQueue and implementation like shown here:

It’s a pretty simple implementation of a C#-based queue, using the ConcurrentQueue class from the System.Collections.Concurrent namespace. The QueueBackgroundWorkItem method will put a Func in the queue for later processing and the DequeueAsync method will pull a Func from the queue and return it.

Next, we need someone to execute the Func work items put on the queue. Add a new class named LongRunningService with the following implementation:

This is an implementation of an ASP.NET Core background service, which is indicated by extending BackgroundService. The service accepts the queue that we implemented in the last step and automatically dequeue and execute work items.

Both the BackgroundWorkerQueue and LongRunningService classes need to be registered with ASP.NET Core. Include the following code in the ConfigureServices method in the Startup.cs file:

That’s it. All we need now is to refactor the CallSlowApi method. The HomeController need an instance of the BackgroundWorkerQueue class injected in its constructor:

The Task.Delay call can be moved inside a Func and handed off to the queue like this:

I simply moved the last two lines of the existing method inside the Func provided for the QueueBackgroundWorkItem method.

Launching the website is now snappy as ever:

To prove that the long-running task is still actually running, you can put a breakpoint after the call to Task.Delay or you can simply inspect the log output in Visual Studio:

For a real-life sample of implementing this, check out our integration with ASP.NET Core here: Error logging and Uptime Monitoring for your web apps

This blog post is brought to you by is error logging, uptime monitoring, deployment tracking, and service heartbeats for your .NET and JavaScript applications. Stop relying on your users to notify you when something is wrong or dig through hundreds of megabytes of log files spread across servers. With, we store all of your log messages, notify you through popular channels like email, Slack, and Microsoft Teams, and help you fix errors fast.

This content was originally published here.