Hosted Services In ASP.NET Core

Hosted Services in the .NET Core world mean background tasks in everyday developer terms. If you’re living in the C# world, and even the Azure world, you actually already have a couple of options for doing background style tasks. Problems that you can solve using Hosted Services are probably similar to the problems you currently solve using Windows Services or Azure WebJobs. So first, let’s take a look at some comparisons.

Hosted Services vs WebJobs

WebJobs are similar to hosted services in that they run on the same machine as an Azure Website – thus sharing resources. WebJobs do have the added benefit of being able to be deployed separately (Hosted services are part of the website) and have additional functionality when it comes to running as a singleton. With Hosted Services, there is an instance running of that hosted service for every deployment of your website which can be an issue if you only want one instance of that “process” running at anytime. You can program around this by creating your own locking mechanism, but obviously webjobs gets this out of the box. The one other difference really is that with the WebJobs SDK, you get things like queue/blob triggers right away. Inside a ASP.NET Core Hosted Service, you would need to write all of this manually.

Hosted Services vs Windows Services

Windows Services are typically hosted on other infrastructure that isn’t also hosting your website. They can be deployed independently, and don’t have really any tie in to your website. But that also comes as a negative. If you are using PAAS on something like Azure or Google Cloud, you would then need a separate VM to hosted your Windows Service. Not too great!

Hello World Hosted Service

Create a new class in your .NET Core Web Project called “HelloWorldHostedService”. This is going to be your first piece of code for your Hosted Service :

So all we are doing is starting a timer that every 10 seconds, prints out “Hello World!” to the debug output.

You’ll notice that we have a start and stop, probably pretty similar to a windows service where the StartAsync method is when our web process kicks off. It shouldn’t be where we do work, but instead where we setup side threads to do the work. And stop is where our web process stops, so we obviously have to pause our timer.

You’ll also need the following line, in your ConfigureServices method, in your startup.cs.

And that’s it, that’s your first hosted service. If you run your web project, your service should kick off (Either set a breakpoint or check the Debug output), and away you go!

Background Service Helper

The thing is, each time you write a background service, it’s likely to be the same sort of process. It’s probably going to either be something on a timer, or in an infinite loop. So 9 times out of 10, you’re going to be using some sort of boiler plate that kicks off your task on a different thread, and handles cancellation tokens etc.

Well Microsoft foresaw this, and create an abstract helper class called BackgroundService. We can rewrite our above code to instead look like :

Notice that we now inherit from BackgroundService and not from IHostedService. But our startup.cs call doesn’t change.

Behind the scenes, the code for BackgroundService looks like (And you can see the full source on Github here) :

So it’s actually pretty simple. It’s just kicking off a thread with your work, and waiting for it to finish (If it finishes). It’s handling the cancellation tokens for you, and a bit of cleanup.

Good Examples Hosted Services

I thought I would end on what makes a good hosted service. It should be something preferably specific to that instance of the website. If you scale out horizontally, each instance of the hosted service shouldn’t compete with each other and cause issues. So things like clearing a shared cache, moving files/data, ETL processes etc may not be great candidates for hosted services because each service will be competing with each other.

An example of a service I wrote recently was that I built an API that read data from a secondary replica database. I didn’t mind if the replica fell behind in updates from the primary, but it still couldn’t fall too far (e.g. More than 15 minutes). If it fell more than 15 minutes, I wanted to force my API to instead read from the primary. Each website checking the secondary status and swapping to read from primary is very scalable because the check itself is atomic and no matter how many times it’s called, it doesn’t interfere with each other.

ENJOY THIS POST?
Join over 3.000 subscribers who are receiving our weekly post digest, a roundup of this weeks blog posts.
We hate spam. Your email address will not be sold or shared with anyone else.

1 comment

Leave a Reply

Your email address will not be published. Required fields are marked *