I recently had to run some code in a background job that takes a few minutes to run. There are lots of different ways to accomplish this with .NET and Azure. But choosing the ideal solution, based on specific needs, is a different story.
This won’t be an article on how to use these solutions; there are other, better resources for that and I’ll link to them where appropriate. Instead, I’ll describe each approach and cover the pros and cons of each, to help inform your decision making.
Each article in the series will look at a different tech. In this article, I’ll focus on a BackgroundService running in an ASP.NET Core project. In future articles, I’ll cover how to accomplish the same thing with Azure Functions, and I’ll also take a look at the good ol’ console app.
Let’s get started.
What Is a BackgroundService?
The BackgroundService class is part of .NET Core. It’s an implementation of the IHostedService interface, which is meant to be used for running background jobs in ASP.NET Core.
All you’ve got to do is inherit from BackgroundService, implement the ExecuteAsync method in your code, hook it up in DI, and you’re set.
You can check out the official docs to see an in-depth explanation of how to use it.
At first glance, it sounds exactly like what I’m looking for. Let’s look a bit deeper at the consequences of using it.
The Good
It’s easy to add a BackgroundService to any ASP.NET Core project since it’s integrated to the core libraries. You don’t need to add any crufty external NuGet packages, or anything else for that matter to get all of its features.
The fact that it runs inside of an ASP.NET Core project makes it easy to expose a health endpoint that says if the service is ready and able to process requests. This is especially useful in a Kubernetes cluster, or any other cloud service that requires a health check endpoint.
It’s trivial for running one-off background jobs that need to run once or recurring tasks based on a timer. A bit more work needs to be put in to run tasks on demand, say by invoking an API endpoint. There is an example in the official docs on how to feed tasks to a BackgroundService through an in-memory queue, and that works for rudimentary tasks.
If you need something more robust to run your background tasks, you’ll likely want to use a Storage Account Queue or Service Bus to enqueue messages that are then dequeued and processed by the BackgroundService. The good news on that front is that the APIs for working with Service Bus have been greatly simplified in the past few years. It’s a few days work to get it up and running in a re-usable way though.
The Bad
It’s not all sunshine and rainbows though. The documentation on how to handle the lifecycle of a BackgroundService is spotty at best. I found some good information in Andrew Lock’s articles, but I would have expected a bit more from the official docs.
The other downside is that adding a BackgroundService to an existing project means you can’t scale the BackgroundService independently of the rest of the ASP.NET Core application, since they are part of the same project. For that reason, you’ll want to isolate the BackgroundService to its own ASP.NET Core project if you think you’re going to need many instances of your worker to process a bunch of background tasks simultaneously.
The Not So Ugly
Overall, BackgroundService is a simple and extensible way to write background tasks. The plumbing required to make it production-grade isn’t too much work either, and for that reason it’s become my go-to solution for writing background tasks.
In the next part, I’ll look at building a background worker through an Azure Function. See you then.