受欢迎的博客标签

ASP.NET Core Background Service-ASP.NET Core Web 6.x application as Windows Services and Linux Services

Published
C:\Users\Administrator2>dotnet --info
.NET SDK (反映任何 global.json):
 Version:   7.0.100-preview.3.22179.4
 Commit:    c48f2c30ee

运行时环境:
 OS Name:     Windows
 OS Version:  10.0.19044
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\7.0.100-preview.3.22179.4\

Host (useful for support):
  Version: 7.0.0-preview.3.22175.4
  Commit:  162f83657c

.NET SDKs installed:
   6.0.202 [C:\Program Files\dotnet\sdk]

step 1: Creating a Default Web Application

We start with new default ASP.NET Core 6.x web application.

<Project Sdk="Microsoft.NET.Sdk.Web">

Step 2: Install the Microsoft.Extensions.Hosting.WindowsServices NuGet package

Install-Package Microsoft.Extensions.Hosting.WindowsServices
Install-Package Microsoft.Extensions.Hosting.Systemd

Install the Worker service on Linux

 

Step 3:Implementing this in .NET 6 with WebApplicationBuilder requires a workaround:

using Microsoft.Extensions.Hosting.WindowsServices;


var webApplicationOptions = new WebApplicationOptions()

{
    ContentRootPath = AppContext.BaseDirectory,
    Args = args,
    ApplicationName = System.Diagnostics.Process.GetCurrentProcess().ProcessName
};


var builder = WebApplication.CreateBuilder(webApplicationOptions);

builder.Host.UseWindowsService(); //for windows Service
builder.Host.UseSystemd();  //for Linux Service

old

 var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

new

using Microsoft.Extensions.Hosting.WindowsServices;


var webApplicationOptions = new WebApplicationOptions()

{
    ContentRootPath = AppContext.BaseDirectory,
    Args = args,
    ApplicationName = System.Diagnostics.Process.GetCurrentProcess().ProcessName
};


var builder = WebApplication.CreateBuilder(webApplicationOptions);

builder.Host.UseWindowsService();

// Add services to the container.
builder.Services.AddControllersWithViews();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();



step 4:Make our app compile down to an EXE

The first thing we need to do is make our app compile down to an EXE. Well.. We don’t have to but it makes things a heck of a lot easier. To do that, we just need to edit our csproj and add the OutputType of exe. It might end up looking like so :

step 3:Install the following package into our Web App 

Inside program.cs, you should have a “CreateHostBuilder” method. You might already have some custom configuration going on,  you  need to tack onto the end “UseWindowsServices()”.

And that’s all the code changes required!

step 4.Deploying Our Service

Open a command prompt as an Administrator, and run the following command in your project folder to publish your project :

g:
cd G:\stock\ReadDZHRealTime\src\WinTimerTask\Stockso.WebHostService
dotnet publish -c Release

 use standard Windows Service commands to install our EXE as a service. run something like so to install as a service :

you only need to run the .bat files from an administrator command prompt.

uninstall.bat :

sc stop Stockso.WebHostService
timeout /t 5 /nobreak > NUL
sc delete Stockso.WebHostService

 

Doing A Self Contained Deploy

We talked about it earlier that the entire reason for running the Web App as a Windows Service is so that we don’t have to install additional tools on the machine. But that only works if we are doing what’s called a “self contained” deploy. That means we deploy everything that the app requires to run right there in the publish folder rather than having to install the .NET Core runtime on the target machine.

All we need to do is run our dotnet release command with a few extra flags :

This tells the .NET Core SDK that we want to release as self contained, and it’s for Windows.

Your output path will change from bin\Release\netcoreapp3.0\publish  to \bin\Release\netcoreapp3.0\win-x64\publish

You’ll also note the huge amount of files in this new output directory and the size in general of the folder. But when you think about it, yeah, we are deploying the entire runtime so it should be this large.

Content Root

The fact that .NET Core is open source literally saves hours of debugging every single time I work on a greenfields project, and this time around is no different. I took a quick look at the actual source code of what the call to UseWindowsService does here. What I noticed is that it sets the content root specifically for when it’s running under a Windows Service. I wondered how this would work if I was reading a local file from disk inside my app, while running as a Windows Service. Normally I would just write something like :

But… Obviously there is something special when running under a Windows Service context. So I tried it out and my API bombed. I had to check the Event Viewer on my machine and I found :

OK. So it looks like when running as a Windows Service, the “root” of my app thinks it’s inside System32. Oof. But, again, looking at the source code from Microsoft gave me the solution. I can simply use the same way they set the content root to load my file from the correct location :

And we are back up and running!