Building a Woker Service
Console Applications have been used for a long time to execute long-running and scheduled tasks, this type of applications do not require a user interface. In the case of scheduled tasks you may need something on top of Console App such as Quartz to make the configuration friendlier.These are some of the use cases I have seen in the past:
- A payroll application that requests timesheets data for employees from another system and calculate salaries to be paid.
- A sales application that pulls sales data at the end of the day, aggregates data, build reports and sends an email.
- An application that is subscribed to an MQTT broker that process messages comning from a sensor and publishes calculated values.
.NET Worker Service Template
Since the introduction of .NET Core 3.0 there is a new template available in the .NET Core SDK called "Worker Service". This template will create a Console Application for you with some usefull feature out of the box. Let's check what templates are available on NET Core 6, the recommended version at the moment of writing this article (Version 7 is on preview status at this moment).
What does this template build for us?
The template mainly creates three files: Program.cs, Worker.cs, and appsettings.json. If we took a look to the Program class we will see that application is using a Host instance. This Host turns a Console Application into a long-running service (Worker Service), it is responsible of starting and stopping any instances of IHostedService inside the application.
The Program Class
This is the entry point of the application. In this class you will find a call to the method
CreateDefaultBuilder which will create an instance of IHostBuilder that will them be build and run.
The host object will provide a single place for configuration for the worker service classes that we will implement and
will also provide features to manage the lifetime of the app.
From the official Microsoft Documentation:
A host is an object that encapsulates an app's resources and lifetime functionality, such as:
- Dependency injection (DI)
- Logging (By default configures ILoggerFactory to log to the Console, debug, and Event Source Output)
- Configuration (Loads IConfiguration from appsettings.json, DOTNET_ prefixed environment variables, environment variables)
- App shutdown (CTRL + C on Console App, Process termination, Progammatic shutdown).
- IHostedService implementations
Worker Services and ASP.NET Core Web Applications share the Host implemtantion since NET Core 3.0, this host implementation replaces the previous one called WebHost. Currently the Kestrel web server is started as a hosted service initialized by the Generic Host.
The Worker Class
The worker class is an implemtation of the abstract class BackgroundService which is at the same time an implemtation of IHostedService.
- Provides code in a while loop for a long running task
- The cancellation token will be used when the app is shuting down.
- BackgroundService abstract class provides basic functionality to start and stop background work.
Creating a Worker Service
Create the solution
Creates a new solution in a folder with the same name.
dotnet new sln -n WorkerService -o WorkerService
Create the Workser Service project
Before taking the next steps, lets navigate to the foler containing the solution.
cd WorkerService
Create the Workser Service project
Creates a new project in the src folder using the Worker Service template.
dotnet new worker -n WorkerService -o src/WorkerService
Add the project to the solution
Adds the project we created in the previous step to he solution.
dotnet sln add src/WorkerService/WorkerService.csproj
Build the application
dotnet build
Run the application
dotnet run --project src\WorkerService
Running the Worker Service as a Windows Service
The Worker Service template creates a Console Application, this type of applications can be run manually by the user as we did in the previous step, but in my experience most of the times we want to run this kind of applications automatically when the server starts. To achieve this we will have to run the Console Application as a Windows Service or a Linux Daemon depending on where we are running this app.
Installing the Console App as Windows Service
There are a couple of modifications we will have to do before being able to run our application as a Windows Service.
-
Add the package Microsoft.Extensions.Hosting.WindowsService, this will us to configure the host to use a WindowsServiceLifetime and ovewrite the default Console lifetime.
-
In the Program class, add a call to UseWindowsService() when creating the Host. Besided overwriting the ConsoleLifetime this method call will set the ContentRootPath to be AppContext.BaseDirectory, and enable logging to the Windows event log.
Publish the application
Once we have finished with the modifications we are ready to publish our application. I have added a couple of parameters to publish as a single file and avoid having pdb files.
dotnet publish -r win-x64 -c Release --no-self-contained -o c:\Publish\win-service /p:PublishSingleFile=true /p:DebugType=None
Run the Worker Service as a Windows Service
For this step we will use sc.exe from the command line. create will create a new Windows Service which adds it to the Windows Registry.
sc create MyWindowsService start=delayed-auto binPath=c:\Publish\win-service\WorkerService.exe