Publishing ASP.NET Core as Windows Service
Hi Friends,
Thought to share one more view with ASP.NET Core hosting as Windows Service. Now, here is the catch. If you have already decided, that you would like to host ASP.NET Core as Windows Hosting, then you probably pick the ASP.NET Core Template on top of .NET Framework as this will only give Windows’ Hosting library references. If you pick this template, then half battle is already over. Then, in that case, you can simply focus on windows service.
But, let’s say you have initially picked up .NET Core template, now you would like to host this as Windows Service. Now, before looking the solution of this, you should know Why you will be picking Windows Service as hosting choice? Only reason to pick windows hosting as you will be considering Windows as your authentication mechanism, which means it can’t be hosted via Kestrel on IIS. Since, Kestrel is meant for cross platform, hence it doesn’t support Windows Hosting. Hence, Kestrel+IIS is not in picture any more.
The other option is self hosting mechanism, where in you copied the folder being produced by dotnet publish to some location on the server and then running dotnet app.dll to run your application. This will also solve the problem, but the problem with this; this will remain cmd window open on server. If accidentally, anybody logs on the server closed this, then in that case, your application will be halted. Then, again you have to run that exclusively by logging in the server.
What advantages we can get from Windows Hosting?
- There will be not any kind of cmd will remain open on server.
- Since, its hosted on windows service, So even if application crashes, it can automatically get started.
- This means, it reduces the overhead of logging on the server, in case app is crashed.
Now, let’s look at the same in action. Below, I have created folder for windows hosting with two files in it.
Let’s have a look at that.
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.WindowsServices;
namespace ExpenseReviewCustomHost
{
public class CustomWebHostService : WebHostService
{
public CustomWebHostService(IWebHost host) : base(host)
{
}
protected override void OnStarting(string[] args)
{
base.OnStarting(args);
}
protected override void OnStarted()
{
base.OnStarted();
}
protected override void OnStopping()
{
base.OnStopping();
}
}
}
And,
using System.ServiceProcess;
using Microsoft.AspNetCore.Hosting;
namespace ExpenseReviewCustomHost
{
public static class WebHostServiceExtensions
{
public static void RunAsCustomService(this IWebHost host)
{
var webHostService = new CustomWebHostService(host);
ServiceBase.Run(webHostService);
}
}
}
With that being said, I also have invocation in program.cs file like shown below.
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.HttpSys;
using System;
using System.Diagnostics;
using System.IO;
using ExpenseReviewCustomHost;
using ExpenseReview_ASPNET;
using System.Linq;
// The default listening address is http://localhost:7000 if none is specified.
namespace ExpenseReview_ASPNET
{
/// <summary>
/// Executing the "dotnet run" command in the application folder will run this app.
/// </summary>
public class Program
{
private static string pathToContentRoot;
#region snippet_Main
public static void Main(string[] args)
{
bool isService = true;
if (Debugger.IsAttached || args.Contains("--console"))
{
isService = false;
}
pathToContentRoot = Directory.GetCurrentDirectory();
if (isService)
{
var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
pathToContentRoot = Path.GetDirectoryName(pathToExe);
}
if (isService)
{
BuildWebHost(args).RunAsCustomService();
}
else
{
BuildWebHost(args).Run();
}
Console.WriteLine("Running demo with HTTP.sys.");
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
#region snippet_Options
.UseHttpSys(options =>
{
options.Authentication.AllowAnonymous = true;
options.Authentication.Schemes = AuthenticationSchemes.NTLM;
options.MaxConnections = 100;
options.MaxRequestBodySize = 30000000;
options.UrlPrefixes.Add("http://localhost:7000");
})
#endregion
.UseContentRoot(pathToContentRoot)
.UseStartup<Startup>()
.Build();
#endregion
}
}
From here, invocation for service starts. Now, if you look at the program, you can see this if program is not launched in debug mode, then service will get triggered with proper path to exe. Till now, we have seen how to create windows service to host our ASP.NET Core Solution. Now, there is one more change which is required here in startup file for authentication as I changed to .NET Framework solution here.
But, while creating the app, I have started as DOTNET-CORE Template, which means I cannot get access to Windows Service libraries.
Now, let’s look at the solution of this. Here, I have modified the .csproj like shown below.
This shows, here I disabled .NET Core 2.0 and changed the target framework to net461, which is .NET Framework 4.6.1, which means now, I will have access to all windows libraries. But, the problem with this, my application starts breaking as other dll references are compatible to .NET Core. Hence to fix that, I have also replaced the same with compatible ones like shown below.
With the following changes in place, now setup is ready. Now, I can build my app. After that, I go ahead and published the same with dotnet publish so that my exe also gets created. After that, you can simply copy that folder somewhere and give the path to executable while creating the service like shown below.
sc create ExpenseService binPath=”D:\publish\ExpenseReview-ASPNET.exe”
This will successfully create the windows service, which you can see by navigating to services.exe like shown below.
With the above change in place, If I navigate to api like http://localhost:7000/api/expense, this will produce me below result.
Now, since this is part of CORS solution, which means APIs are getting consumed in Angular 4 App, hence app will look like
I hope you would have liked today’s discussion. In coming posts, will delve further into the guts of ASP.NET Core. Hence, till then stay tuned and Happy Coding.
API Link:- https://github.com/rahulsahay19/ExpenseReview-API/tree/Windows-Hosting
Thanks,
Rahul Sahay
Happy Coding
Originally published at myview.rahulnivi.net on October 25, 2017.