.NET Core 2.1 had a ton of cool stuff in it. David Fowler did a bunch of tweets a while back on some of the hidden gems in this release and one that really jumped out at me was the ability to host an ASP.NET Core application in a Windows Service. This post is going to walk through creating a new ASP.NET Core application and then making the changes needed for it to run as a Windows Service. I pulled most of the information I needed from the official docs.
Project Creation
We will be using the .NET CLI to create the project, but you can also use Visual Studio if you like. Open a command prompt in the directory where you want the project created and run the following commands.
dotnet new razor --no-https dotnet new sln dotnet sln add WindowsServiceHosted.csproj
Open the new solution in Visual Studio.
Project File Changes
Right click on your project field and select Edit.
The first step is to add a runtime identifier. The docs are using win7-x64 so we are going to use the same. I did try using win and win7 but they don’t work since there isn’t a specific runtime associated with them. In this same step, we are going to add a reference to the Microsoft.AspNetCore.Hosting.WindowServices NuGet package.
Before: <Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp2.1</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.App" /> </ItemGroup> </Project> After: <Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp2.1</TargetFramework> <RuntimeIdentifier>win7-x64</RuntimeIdentifier> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.App" /> <PackageReference Include="Microsoft.AspNetCore.Hosting.WindowsServices" Version="2.1.1" /> </ItemGroup> </Project>
Program Class Changes
Open the Program
project’s class. In the Main
function, Run
call on the Web Host needs to change to RunAsService
.
Before: CreateWebHostBuilder(args).Build().Run(); After: CreateWebHostBuilder(args).Build().RunAsService();
Next, in the CreateWebHostBuilder
function, we need to change the content root to be the directory of the application. We are using the Process
class to pull the filename and using that to get the directory the process is running in.
Before: public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>(); After: public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseContentRoot(Path .GetDirectoryName(Process .GetCurrentProcess() .MainModule .FileName)) .UseStartup<Startup>();
Publish
In the pre-core versions this step could have been skipped, but for .NET Core the only way to get an actual exe out of your project that can be installed is to publish the application. You can either use the .NET CLI or Visual Studio to publish the application. I’m going to use the following .NET CLI command run from the same directory as the project file.
dotnet publish
Since I didn’t specify a configuration value the project was built in debug and ended up in the bin\Debug\netcoreapp2.1\win7-x64\publish directory.
Installation
Open a command prompt in admin mode and run the following command to create a windows service. The binPath needs to be the full path to your exe or your service will fail to start even it is created successfully.
sc create WindowsServiceHosted binPath= "C:\WindowsServiceHosted\bin\Debug\netcoreapp2.1\win7-x64\publish\WindowsServiceHosted.exe"
Also, note that the space after binPath= and before the exe name is needed.
Service Management
Now that the service is installed run the following command to start it.
sc start WindowsServiceHosted
After the service is started you can open a browser and go to http://localhost:5000/ and see your application running.
To check the state of your service use the following command.
sc query WindowsServiceHosted
To stop your service use the following command.
sc stop WindowsServiceHosted
Finally, to uninstall your service use the following command.
sc delete WindowsServiceHosted
Debugging
While debugging a Windows Service can be done, it is a pain. Thankfully the docs walk us through away to run the application normally if it is run in debug mode, but this does require more changes in the Program
class.
First, change the CreateWebHostBuilder
back to its original state.
public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>();
Next, in the Main
function, we have to decide if we are running as a service or not and if so switch around how we start the application.
public static void Main(string[] args) { var runAsService = !Debugger.IsAttached; var builder = CreateWebHostBuilder(args); if (runAsService) { builder.UseContentRoot(Path .GetDirectoryName(Process .GetCurrentProcess() .MainModule.FileName)); } var host = builder.Build(); if (runAsService) { host.RunAsService(); } else { host.Run(); } }
For this example, we are going to run as a service only if the debugger isn’t attached. You can see how the rest of the codes is using this runAsService
boolean to change between the setup needed for a service and that of a normal web application host.
Wrapping Up
I’m very happy to have the ability to host an ASP.NET Core application as a Window Service. This seems to be a case I run into a lot more that one would think.
I hope to see things that simplify Windows Services like Topshelf add support for .NET Core 2.1 (this issue has links to all the issues related to 2.1 if you want to check on the progress). It will be nice to have .NET Core Windows Services with the same level of support and the previous version of .NET.
Also published on Medium.