With the release of .NET Core 3.0, the ASP.NET team introduced a new “Worker Service” project template, which is available as part of the SDK. In this post, I’ll introduce the new template, along with some practical examples of the kinds of services which I develop using it.
If you find the information in this post useful and plan to build your own worker services, may I suggest viewing my new course on Pluralsight – “Building ASP.NET Core Hosted Services and .NET Core Worker Services“. I dive deeply into worker services and demonstrate how to refactor parts of a web application as microservices.
What is a .NET Core Worker Service?
A worker service is a .NET project built using a template which supplies a few useful features that turn a regular console application into something more powerful. A worker service runs on top of the concept of a host, which maintains the lifetime of the application. The host also makes available some familiar features, such as dependency injection, logging and configuration.
Worker services will generally be long-running services, performing some regularly occurring workload.
Examples of worker services:
- Processing messages/events from a queue, service bus or event stream
- Reacting to file changes in an object/file store
- Aggregating data from a data store
- Enriching data in data ingestion pipelines
- Formatting and cleansing of AI/ML datasets
It’s also possible to develop a worker service which performs a process from start to finish and then shuts down. Coupled with a scheduler, this can support periodical batch workloads. For example, every hour the service is started by the scheduler, calculates some aggregate totals and then shuts down.
Worker services do not have a user interface, nor will they support direct user interaction. They are particularly applicable when designing a microservices architecture. Here responsibilities are often split into distinct, separately deployable and scalable services. It’s not uncommon to have numerous worker services as your microservices architecture grows and evolves.
What Does The Worker Service Template Provide?
It’s entirely possible to develop a long-running worker service without using the worker service template. I was doing so in early versions of .NET Core, manually establishing a host with a dependency injection container and then initiating my processing workloads.
The worker service template includes useful foundational components, like dependency injection, by default so that we can focus on building our business logic on top. It includes a host which manages the lifecycle of the application.
The worker service template is reasonably basic and includes just three core files out of the box.
The first of which is a
Program class. This class consists of the required Main method entry point for .NET console applications. The .NET runtime expects to locate this method within the Program class when it starts your .NET application.
Included within the Program class, as part of the worker service template, is the
CreateHostBuilder private method which creates an
IHostBuilder. The IHostBuilder interface defines a type which uses the builder pattern to produce an instance of an
IHost. The template creates a new HostBuilder by calling the static
CreateDefaultBuilder method on the Host class.
It then uses the builder to configure the IHost, which will be used to run the worker service application. The host provides features such as a dependency injection container and logging, just as we can use in ASP.NET Core applications. In fact, since .NET Core 3.0, ASP.NET Core web applications and .NET Core worker services both run on the same IHost.
It’s possible to register your own types with the dependency injection (DI) container. This is the same DI container as has been available in ASP.NET Core since it’s first release. If you’d like to learn more about dependency injection in ASP.NET Core (and .NET Core worker services), I cover its features and usage in great detail in my Pluralsight course – Dependency Injection in ASP.NET Core.
There’s one service registration included by default which I’ll come back to a little later in this post. Let’s not worry about it for now.
The CreateDefaultBuilder method is called from within the Main method and is used to build and then immediately run the host. When the runtime calls the Main method, the application will be started, and the host will keep it running, listening for typical shutdown signals such as the CTRL+C keys being pressed.
The appsettings.json file will be very familiar if you have previously used ASP.NET Core. It is one of the common sources for application configuration. The host is configured to load application configuration from several sources when the application starts using any registered configuration providers. One such provider loads configuration from the appsettings.json file. This file contains JSON, structured to contain keys and values representing application configuration. These values can optionally by defined within sections which logically group related configuration.
In worker services, the same configuration sources, including this appsettings.json file and environment variables will be inspected at startup, with the final configuration being built from the various sources. A number of default providers, and therefore sources, are loaded by default. If you need to, it’s possible to customise the providers which the host uses to load configuration data.
Configuration is another core topic for ASP.NET Core and worker service applications and is also available as a Pluralsight course.
The default appsettings file in the template includes configuration settings for the logging library, which is available by default for worker services. The configuration here sets log levels for some of the logging contexts.
Worker class is something new which you will not find in the default ASP.NET Core project template. This is where the magic of hosted services, combined with the host, provide the basis of a worker service.
Let’s take a peek at the code.
This class derives from the
BackgroundService abstract base class. The BackgroundService class implements an interface named IHostedService. In my now outdated post from July 2017, I introduced the IHostedService interface and demonstrate how one could implement it to start a long-running task.
David Fowler kindly provided the implementation in that post and that code eventually formed the final BackgroundService class which is now included as part of the hosting abstractions package.
BackgroundService includes an abstract method named ExecuteAsync which we must override in our subclass, just as the worker class, provided in the worker service template, does here. ExecuteAsync returns a Task which internally the BackgroundService expects to be some long-running workload. It will start the Task, which then runs in the background.
Internally, the host will start any registered implementations of IHostedService. This includes types which derive from the BackgroundService abstract class. Remember, BackgroundService implements IHostedService for us.
How do I Register an IHostedService?
The next logical question is, how do I register an IHostedService? If we head back to the code from Program.cs, we’ll find out.
Inside the ConfigureServices method, types can be registered with the dependency injection container. An extension method is defined for the IServiceCollection called
AddHostedService, which allows us to register a class which implements IHostedService.
The template has already registered the Worker class as a hosted service.
At startup, the host will locate all registered instances of IHostedService and start them in order, at which point, their long-running workloads run as background Tasks.
Why Build .NET Core Worker Services?
The simple answer is – when and if you need them! If you have a requirement to develop a microservice which has no user interface and which performs long-running work, then a worker service is very likely going to be a good fit.
Remember that a worker service is just a console application under the hood. That console application uses a host to turn the application into something which runs until it is signalled to stop. The host brings with it features like dependency injection that you’ll likely already familiar with. Using the same logging and configuration extensions available in ASP.NET Core makes it easy to develop worker services which should log information and which require some configuration. These requirements are nearly always present when building worker services that will run in the cloud. For example, you will likely need to provide configuration for any external services that your worker service will interact with. A queue URL for example.
Worker services can be used to extract responsibilities from existing ASP.NET Core applications (I cover this in my Pluralsight course) and to design new .NET Core based microservices.
In this post, I introduced the worker service project template and some potential use cases for worker services. We explored the three default files that are included in new projects created using the worker service template.
What Files Are Part of the Worker Service Template?
- Program.cs – Entry point for the console application. Creates and runs a host to manage the application lifetime and make a long-running service.
- appsettings.json – A JSON file which provides application configuration values.
- Worker.cs – Derives from the BackgroundService base class to define long-running workloads which are executed as background Tasks.
What Are Worker Services?
- Applications which do not require user interaction.
- Use a host to maintain the lifetime of the console application until the host is signalled to shut down. Turning a console application into a long-running service.
- Include features common to ASP.NET Core such and dependency injection, logging and configuration.
- Perform periodic and long-running workloads.
If you enjoyed this content, you’d undoubtedly enjoy my new Pluralsight course “Building ASP.NET Core Hosted Services and .NET Core Worker Services“.