Introduction to Worker Services in .NET Core 3.0

Nick Fane
Nick Fane
Jun 12 · 6 min read

.NET Core 3 is on the horizon and with it comes a plethora of new features, one of which is a new project template for .NET Core Worker Services.

Before we begin, be aware that it’s been possible to create these services since .NET Core 2.1 using IHostedService, this release only adds the project template and minor refinements. However, since this appears to be a bit of a milestone in .NET Core and background workers, I thought I’d write up a few articles for those who haven’t yet worked with them and wanted to dive in.

About myself, I’ve been working with .NET for about six years now and through either luck or good fortune, have been able to build many production applications with .NET and ASP.NET Core since the initial stable release.

As much fun as it’s been, one of the most frustrating experiences has been creating background workers or windows services before .NET Core 2.1. In a previous company, we used the framework Topshelf (albeit as a means of running these workers as windows services), but they were building support for when .NET Core hit 2.0. Other frameworks such as Hangfire could have been used to reach our desired outcome, but with the continually changing landscape of .NET Core 1.x and Docker on the rise, we decided to go with regular old .NET Core Console applications running in containers.

While this approach worked well for us at the time, it always felt hacky and in my opinion, never gave .NET a strong argument for being the ‘right tool for the job’ when it came to creating these types of services.

First of all, why use Worker Services?

Worker services are the perfect use case for any background processing such as components in an ETL pipeline, processing messages from a Kafka, Rabbit or SQS queue. Perhaps you want to run customised health checks on your systems. Any processing job you can think of where you need a simple, straightforward framework to do so, Worker Services now has you covered.

Getting Started

You can bootstrap a new project by going File > New Project > ASP.NET Core Web Application (which contains the Worker Service template while .NET Core 3.0 is in preview), creating your service then choosing the Worker Service template from the list

The first thing you’ll notice is that the template is as minimal as possible with only three files (or four if you’ve added Docker support), so if you’re like me and hate having tons of bootstrapping files in your project templates, then you’ll love it.

Pretty damn minimal

If you’ve been around .NET Core for a while, the program.cs file should be pretty straightforward, but if not, then here’s a quick explanation.

In short, we have two methods, Main which is the entry point to our application, passing in any additional arguments to our runtime and CreateHostBuilder which will bootstrap our application, configure our services and setup any dependencies that we need.

The CreateHostBuilder is where you can add your configuration management, IoC or any additional startup configuration you may need.

The Worker

Default worker with IoC and a lifecycle.

The template utilises IoC to inject a logger into our service and manages its lifecycle via a CancellationToken that’s passed in by the .NET Core framework. Most worker services wouldn’t differ too much from this implementation unless of course, you were using an external source (i.e. feature flag) to handle the lifecycle.

Running the worker

Running with Docker

Dockerfile for Windows containers

At first it may seem like a lot of steps, but the breakdown is quite simple. We start by pulling a nanoserver runtime image to that we’ll use when we run our container and use the alias base.

Next, we pull from the SDK equivalent of that nanoserver image so we have access to the .NET Core SDK. We choose our WORKDIR, copy our project files into it, then run our dotnet restore and build commands.

We then dotnet publish our application’s DLL’s in a release configuration to the /app directory of our build image. Lastly, we come back to our base image, and copy our published DLL’s into the /app directory of the base image and assign the ENTRYPOINT so docker knows how to run our application.

Running as a windows service

Start by downloading the Microsoft.Extensions.Hosting.WindowsServices NuGet package and adding it to your project

The current (3.0.0.5) preview package for hosting Windows Services

Now inside your program.cs add UseWindowsService() to your CreateHostBuilder

Now we can create a service by running a dotnet publish on our project, then use the sc.exe utility to create a service. Here’s a batch script I’d include in my projects to automate this

dotnet restore
dotnet publish -c Release -o ./publish/workerservice
sc.exe create MyWorkerService binpath= ./publish/workerservice/worker-preview.exe

Run this from the projects root directory

And if all goes well, You should see your Windows Service created and runnable from the service manager!

The worker service waiting to run

Conclusion

Up Next

Nick Fane

Written by

Nick Fane

Software Engineer based in Sydney