.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 aren’t anything new or revolutionary. If you’ve written software for any amount of time, chances are you’ve knowingly (or unknowingly) created many yourself! If you’ve ever written something as simple as a long-running console application or had a thread in an API pulling off a queue or shared resource, then you’ve pretty much built your own.
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.
Nowadays it’s a breeze to build these services, to follow this guide, go ahead and download the .NET Core 3.0 Preview as well as the Visual Studio Preview to begin using it (or enable previews in your current Visual Studio, whatever best suits you).
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.
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.
You will notice the template has already provided an example of a basic worker service, with IoC and it’s own managed 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
These workers can be run in the same fashion that you’d run a Console Application (or most .NET Core applications), use the .NET Core CLI, publish the solution/project and call dotnet run on the DLL that is produced by your project. You can run it in a container by adding an appropriate Docker file and running docker build and docker run on the file.
Running with Docker
Running the service in a container is the same as running any other .NET Core applications. If you’ve added Docker support (in my case I chose Windows containers) when you started your project you’ll be created with a Dockerfile similar to the one below.
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
If you’re looking to run as a Windows service, you’ll need to configure the template slightly.
Start by downloading the Microsoft.Extensions.Hosting.WindowsServices NuGet package and adding it to your project
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 publish -c Release -o ./publish/workerservice
sc.exe create MyWorkerService binpath= ./publish/workerservice/worker-preview.exe
And if all goes well, You should see your Windows Service created and runnable from the service manager!
Awesome! If all went well, you should be ready to build some Worker Services using .NET Core 3. If you’ve learnt something from this tutorial, please share it with anyone you think could use this information! Stay tuned for further tutorials on .NET Core Worker Services and thanks for reading!
Queue Processing with .NET Core Worker Services
Deploying .NET Core Worker Services (coming soon)