Running a .NET Core Generic Host App as a Windows Service

I received a question at the weekend via a comment from Vadim on my Implementing IHostedService in ASP.NET Core 2.0 post. Vadim asked the following:

We have a lot of console applications that are listening to a queue and performing some work. In .net 4.6 we would make some of these classes inherit from ServiceBase class. Then we would use the sc.exe tool on the server to install these as Windows Services.

We are looking for a similar solution using .net core. The problem is, ServiceBase does not exist in .net core.

It seems like IHostedService is a good alternative. However, the one thing I don’t understand is, how do we take a .net core application with one or more IHostedServices and install them as a windows service? We want to be able to start/stop these applications from some kind of UI and we want them to always be running when the server is up.

This piqued my interest. I could vaguely recall reading something which described the process required to run an ASP.NET Core application as a Windows service. Off I went to Google and found the documentation which I’d previously read. The documentation was focused on running an ASP.NET Core app, hosted on Windows, as a Windows service. This looked like a good starting point.

I created a basic .NET Console application, using the Generic Host pattern and took a look to see if the RunAsService extension, mentioned in the ASP.NET Core documentation, would work. Turns out, it doesn’t, as it’s not available on the IHostBuilder interface, only for the IWebHostBuilder. Next, I thought I’d take a look at the ASP.NET Core Hosting repository to see if I could figure out if this was available under a different name or failing that to see if there were any plans for similar functionality for IHostBuilder. I came across this issue where Chris Ross (aka Tracher) linked to his GenericHostSample application.

Armed with that sample code I set about adding the relevant classes to my own app. It was mostly a copy/paste exercise; so full credit goes to Chris Ross for his sample code.

My next steps were then based on the ASP.NET Core documentation. However, as that document is specific to ASP.NET Core, I thought it would be valuable to summarise the steps required for running a generic host .NET core app as a Windows service here on my blog. I won’t cover everything in as much detail as the full documentation which I suggest you also go and read for additional context and explanation.

NOTE: A complete sample for a basic generic host based .NET Core Windows service can be found on my GitHub repository.

Project File

After creating a new .NET Core console application, the first thing we need to do is to add a reference to the Microsoft.Extensions.Hosting package and the Microsoft.AspNetCore.Hosting.WindowsServices System.ServiceProcess.ServiceController package (thanks to David Fowler for a PR to tidy up my dependencies in the sample).

As well as these package reference, you’ll see that in the main PropertyGroup section we’ve also set the RuntimeIdentifier to win7-x64. The RID (Runtime Identifier) in this case identifies that the target platform for this application is Windows. For more information about RIDs see the documentation. Since Windows Services are only available on Windows, it makes sense to set a windows target platform which will also ensure we produce a .EXE rather than .DLL file when building our application.

In the above sample we can also set the LangVersion to 7.1 to allow me to use an async Main method.

Program.cs

Next we need to setup our application entry point in Program.cs. The Main method is our entry point and in my sample is as follows:

I’ve based this example on the “Host ASP.NET Core in a Windows Service” documentation.

First it performs a check to see if we’re either debugging or if the application has been started having been passed an argument of “–console”. In that case we set the isService flag as false. We’ll use this later to allow us to debug the application from Visual Studio as a standard console application.

Next we create a HostBuilder which we’ll use to create the generic host. In here we register one service which is an implementation of the IHostedService interface. This is how we’ll run code within our application. See my earlier IHostedService post for more detail on how this interface can be used to run background workloads. We’ll look at the implementation for this application a little further down.

Finally, using the isService flag, we now call either the RunAsServiceAsync extension on the builder, or the RunConsoleAsync. The later will run the application as a normal .NET Core console app, perfect for local testing and debugging. The former is the extension method I was missing earlier. This comes from two files I have added, copied from the GenericHostSample mentioned earlier.

ServiceBaseLifetime.cs

I won’t go into the internals of this too deeply. This is entirely copied from the Microsoft sample. This class derives from the ServiceBase class used to define Windows services. It also implements the IHostLifetime interface from Microsoft.Extensions.Hosting.

ServiceBaseLifetimeHostExtensions.cs

This class is also copied from the GenericHostSample and adds the RunAsServiceAsync extension method which will ensure the ServiceBaseLifetime is registered as the implementation for IHostLifetime.

FileWriterService

This class implements IHostedService and once registered will be started and stopped by the IHost. We follow the generic host documentation and include a Timer which will fire every minute. It will run the code in WriteTimeToFile method which appends the current time to a file. Not particularly useful, but for this example it’s enough to show that something is actually happening. In the original question from Vadim the requirement was to poll a queue and process messages. We could just as easily perform that work here too.

At this point we now have our complete sample application. We can run this directly in Visual Studio to test it, but our main goal is to register this as a Windows service.

Again, if you want to download the source yourself you can do so from GitHub.

Creating the Windows Service

The final step is to build our code and register a service from the executable. We’ll follow the ASP.NET Core documentation to build our code and register a Windows service. Again, I won’t cover the detail here since the referenced documentation provides ample explanation. To summarise though, the steps are as follows:

Build and publish the project from the command line. This command can be run in the directory in which the project resides…

dotnet publish --configuration Release

Next, use sc.exe to create a service, passing the full path of the built executable…

sc create MyFileService binPath= "E:\Projects\IHostedServiceAsAService\IHostedServiceAsAService\bin\Release\netcoreapp2.1\win7-x64\publish\IHostedServiceAsAService.exe"

Next, use sc.exe to start the service (this needs to occur in a command prompt running as Administrator)…

sc start MyService

At this point, our service is running. In my case, I confirmed that a text file called TestApplication.txt appeared in my D: drive (that destination and filename is hardcoded in my sample), which confirms that the service is running as expected. Every minute, while the service is running, I can see a new line added to the text file.

Summary

If you’ve followed along, you should now have a .NET Core based Windows service running on your machine. As per the requirement in the original question, you could easily modify this service to read from a queue and process the messages it receives. Personally, I tend to deploy such services as a basic console application, running in Linux based Docker containers. For production workloads, this enables easy scaling and I can manage the services through our container orchestration in AWS ECS. However, should you need a Windows service, then hopefully this post is enough to get you started.

Hopefully, in a future release of ASP.NET Core, this functionality will be included in the framework. If that happens we won’t need to include our own ServiceBaseLifetime and ServiceBaseLifetimeHostExtensions classes. Based on a reply from Chris in the original GitHub issue, that looks like something which may be considered in the 3.0 timeframe as part of some wider hosting work they plan to do.

Using HostBuilder and the Generic Host in .NET Core Microservices Exploring a simple pattern for cross-cutting concerns in console based services.

TL;DR;

The “generic” Host and HostBuilder are components  of a new feature set coming with the release of .NET Core 2.1. A use case of them is to simplify the creation of console based services by providing a pattern for adding cross-cutting concerns such as dependency injection, configuration and logging.

Introduction

Since ASP.NET Core 1.0 was released we’ve had the WebHostBuilder class which allows us to configure and build a WebHost. This then handles the lifetime of the application while the server (Kestrel) accepts and processes HTTP requests. In ASP.NET Core 2.0 the WebHostBuilder got some further refinement and simplification. The WebHostBuilder allows us to do things such as configuring services with a dependency injection container; quite often the container provided by Microsoft as part of ASP.NET Core. The WebHostBuilder also allows us to load configuration from multiple sources into a final configuration representation of Key/Value pairs.

The works extremely well for ASP.NET Core web applications, but there were no similar options in the framework for other types of application, until now!

NOTE: Please bear in mind that this post is written based on the ASP.NET Core 2.1 preview 1 release. Therefore, things may change during the public previews and also before the final release of 2.1 based on feedback received during those previews.

Introducing IHost and the HostBuilder

A new option available to developers working with .NET Core 2.1 is the new “generic” Host which enables developers to easily set up cross-cutting concerns such as logging, configuration and dependency injection for non-web focused applications. The team have realised that having the host tied to the concern of HTTP was perhaps not an ideal solution since many of these things are common requirements in other application types.

An example of where this could be used is in a console application which needs to run background processing tasks, perhaps handling messages on a queue for example. These types of services are now pretty common in a cloud native, container based architecture.

In the current 2.0 version of .NET Core it is certainly possible to utilise the logging, configuration and DI libraries within a console application. At work we have a number of microservices which do things such as processing messages from queues and data enriching tasks. We have to manually include and setup each of those common concerns ourselves.  Although this is possible, there’s some plumbing required to get things like DI setup within the application.

Building a Host

To create a Host we can use the new HostBuilder, which has a similar set of methods and extensions as the existing WebHostBuilder. The patterns should therefore be familiar to anyone working with ASP.NET Core currently.

There is one main difference to be aware of. The HostBuilder doesn’t provide an extension method that allows you to use a startup class as we can with the WebHostBuilder. This decision was made primarily to avoid the need to create two separate DI containers behind the scenes. With the generic host, a single service collection is configured and then used to build a the final service provider.

In the Main method for your application you can start by creating a HostBuilder and then use extension methods to register services with DI, read configuration and configure the logging that you need for your application.

The best way to explain the feature is with an example. If you want to view the full sample code you can pull it from GitHub.

If we take a look at the Main method for this console application, we can explore the creation of a Host for our application.

NOTE: I’ve used the async Main method here which is available since C# 7.1. By default, new applications will only be enabled for 7.0 so this code may not immediately compile. See the Microsoft documentation on ways to configure the C# language version for more information.

If you’ve used ASP.NET Core at all and have seen the WebHost builder, particularly in the 1.0 time frame, this might look quite familiar. We start by creating a HostBuilder which we can then use to define the Host we want to create. The first method in this example is the ConfigureAppConfiguration method. This method allows us to configure which configuration providers should be used to construct the final representation of configuration values for our application.

This is identical to the way that configuration can be customised when using the WebHostBuilder. In this example we have said that we want configuration values to be first read from an appsettings.json file, followed by environment variables and finally from any arguments passed into the application.

Next we call ConfigureServices which just as with the WebHostBuilder, allows us to register services with the ServiceCollection. Registration is performed using extension methods on the ServiceCollection and once complete, will enable us to get instances of those registrations wherever DI is available in our application.

In this case the first of these adds the ASP.NET Core Options services and the second sets up the registration for the IOptions binding. The final service registration is something I’ll come to a little later on.

The final section, ConfigureLogging as you might expect sets up logging for the application. In this case we add console logging, which uses the values from the application configuration to determine what to log.

The logging config in this sample is the same as found in a default ASP.NET Core web applications created using the templates.

The final step is to call RunConsoleAsync on the HostBuilder which builds and starts the application. It will then keep running until CTRL+C is used to trigger it to shutdown.

Getting Stuff Done

A service wouldn’t be much good if we left it here. At this point we just have a console application running, but not actually doing anything useful. Therefore we need a way to define the work which our application should perform.

The pattern that is recommended for this style of service is to utilise the new IHostedService feature, first introduced in ASP.NET Core 2.0. I wrote about this in a previous blog post.

Here we have a basic IHostedService implementation which will be run within this service…

I won’t go too deep into this code but I will summarise what it’s doing. When the application is started, it will call StartAsync on this service. Within that method we create a Timer which does some work every five seconds.

The work itself is defined in DoWork. Here is simply users the ILogger to log a message as information. This includes a message retrieved from the application configuration. This is accessed through the IOptions object passed into the service by DI.

At shutdown, StopAsync is called and the service cleans up a little before the application is killed. This is quite a contrived example but I wanted to keep things simply and focus on how the pieces fit together.

With the IHostedService implementation defined we simply have to register it with the DI container using the following common in ConfigureServices (which we saw earlier).

services.AddSingleton<IHostedService, PrintTextToConsoleService>();

We could add multiple hosted services if we needed to have various things running within this service.

Summary

There are quite a few cases for using this new “generic” Host concept. In this post we’ve explored a quite basic example, however I wouldn’t need much more than this to simplify a few of the microservices in our environment. Having a single common pattern for web applications as well as services, with easy access to things like DI, logging and configuration is extremely welcome.