目录

  • 基于 Web 应用的服务
  • 发布
  • 脚本
  • 安装为服务并运行
  • 停止并卸载服务
  • 安装为 WindowsServer
  • 删除安装失败的服务
  • 重新发布安装
  • 卸载服务
  • 服务仅执行后台任务
  • 脚本
  • 依赖部署 VS 独立部署
  • 总结
  • 第一步:引用包
  • 第二步: 修改 Program.cs
  • AspNet Core 3.x、5.x
  • AspNet Core 6.0
  • 后台服务 Worker:BackgroundService
  • 第三步:发布项目
  • 第四步:创建windows服务
  • 第五步:启动 windows服务
  • 第六步:停止和删除 windows服务

如果不想看详细的操作步骤,直接看本文结尾的【总结】

官方文档:
https://docs.microsoft.com/zh-cn/aspnet/core/host-and-deploy/windows-service?view=aspnetcore-6.0&viewFallbackFrom=aspnetcore-3.0&tabs=visual-studiohttps://docs.microsoft.com/zh-cn/aspnet/core/host-and-deploy/windows-service?view=aspnetcore-6.0&tabs=visual-studiohttps://docs.microsoft.com/zh-cn/dotnet/core/extensions/windows-service 官方示例:
.Net5及其以下版本:https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/host-and-deploy/windows-service .Net6.0:https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/host-and-deploy/windows-service/samples/6.x/WebAppServiceSample/Program.cs

.Net6.0 写法不同有 .Net5

部署为windowsServer,首先要看网站是基于什么类型的!
对于使用 Razor Pages 或 MVC 框架的基于 Web 应用的服务,请在项目文件中指定 Web SDK:

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

如果服务仅执行后台任务(例如 托管服务),请在项目文件中指定辅助角色 SDK:

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

不同的网站类型,选择不同的的依赖包;

<!--托管服务部署时的依赖-->
    <PackageReference Include="Microsoft.AspNetCore.Hosting.WindowsServices" Version="6.0.2" />
    <!--基于 Web 应用的服务部署时的依赖-->
    <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />

当然可以这两种的网站类型是可以同时存在,官方示例,就是这样子的,

项目工程文件:

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

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <!--依赖框架的部署 (FDD)-->
    <IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
  </PropertyGroup>

  <ItemGroup>
    <!--托管服务部署时的依赖-->
    <PackageReference Include="Microsoft.AspNetCore.Hosting.WindowsServices" Version="6.0.2" />
    <!--基于 Web 应用的服务部署时的依赖-->
    <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
  </ItemGroup>

</Project>                                                                             |

Program.cs

// 托管服务
builder.Services.AddHostedService<ServiceA>();

// 基于 Web 应用的服务
builder.Host.UseWindowsService();

其中,托管服务 ServiceA 的定义如下:

public class ServiceA : BackgroundService
    {
        public ServiceA(ILoggerFactory loggerFactory)
        {
            Logger = loggerFactory.CreateLogger<ServiceA>();
        }

        public ILogger Logger { get; }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            Logger.LogInformation("ServiceA is starting.");

            stoppingToken.Register(() => Logger.LogInformation("ServiceA is stopping."));

            while (!stoppingToken.IsCancellationRequested)
            {
                Logger.LogInformation("ServiceA is doing background work.");

                await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
            }

            Logger.LogInformation("ServiceA has stopped.");
        }
    }

基于 Web 应用的服务

创建 AspNet Core 项目,项目名称为:WebWinServer1
添加包 Microsoft.Extensions.Hosting.WindowsServices

接着修改 Program.cs
AspNet Core 3.x,AspNet Core 5.x的写法

public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseWindowsService()//指定项目可以部署为Windows服务
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>()
                        .UseUrls("http://*:8787"); //使用的端口号
                });

AspNet Core 6.0 的写法
由于 AspNet Core 6.0 自身存在的一个 bug,见:https://github.com/dotnet/AspNetCore.Docs/issues/23387 当 AspNet Core 6.0 项目部署为 Windows服务后,由于这个bug导致程序找不到项目文件,最终Windows启动会失败!!!

如何解决呢?在上面的这个issues下,开发者 davidfowl 给出了两个解决方案:

第一种方案

var options = new WebApplicationOptions
{
    Args = args,
    ContentRootPath = WindowsServiceHelpers.IsWindowsService() ? AppContext.BaseDirectory : default
};

var builder = WebApplication.CreateBuilder(options);
builder.Host.UseWindowsService();
//......

详见官方示例:https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/host-and-deploy/windows-service/samples/6.x/WebAppServiceSample/Program.cs

第二种方案
在创建 windows服务的时候,额外添加 --contentRoot <执行程序所在目录> 参数,即:

sc create AspNetCore-WinServer binPath= "E:\webpulish\web-winserver\WebWinServer1.exe --contentRoot E:\webpulish\web-winserver"

如果你已经创建了windows服务,也可以通过如下命令修改配置:

sc config AspNetCore-WinServer binPath= "E:\webpulish\web-winserver\WebWinServer1.exe --contentRoot E:\webpulish\web-winserver"

注:其中,目录:E:\webpulish\web-winserver 是项目的发布目录,见 下一节发布

发布

右键工程文件,在菜单中选择【发布】,设置发布配置

手把手教 net core 部署nginx .net core windows 部署_Web

注意,这里的
部署模式:选择【框架依赖】,目标框架选择 netcoreapp3.1

但是我的电脑没有 .netcore3.1的运行时,之所以这样设置,只是为了测试和模拟真实的生成环境可能遇到的情况
多个网站版本不同,需要安装各个版本的运行时,这有不太可能,除非使用Docker部署。

其中,发布文件地址是:

E:\webpulish\web-winserver

然后点击发布

手把手教 net core 部署nginx .net core windows 部署_Web_02

脚本

参见:https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/host-and-deploy/windows-service/samples/scripts

安装为服务并运行

创建安装脚本文件install.bat

set serviceName=AspNetCore-WinServer
set serviceFilePath=%cd%\WebWinServer1.exe
set serviceDescription=AspNetCoreweb程序部署为WindowsServer
sc create %serviceName% binPath=%serviceFilePath%
sc config %serviceName% start=auto  
sc description %serviceName% %serviceDescription%
sc start %serviceName%
pause

停止并卸载服务

创建卸载脚本文件uninstall.bat

set serviceName=AspNetCore-WinServer
sc stop  %serviceName%
sc delete %serviceName%
pause

安装为 WindowsServer

把上面创建的安装脚本 install.bat 和卸载脚本 uninstall.bat 复制到发布文件根目录,示例中的目录是:

E:\webpulish\web-winserver

使用管理员身份打开CMD,执行安装脚本 install.bat

注意:一定要是管理员身份打开,否则报错:

[SC] OpenService 失败 5:拒绝访问

>E:
>E:\>cd E:\webpulish\web-winserver
>E:\webpulish\web-winserver>install.bat
....
E:\webpulish\web-winserver>sc.exe start AspNetCore-WinServer
[SC] StartService 失败 2:

或者手动输入命令创建windows服务
AspNet Core 3.x、5.x 创建windows服务的命令如下:

E:\webpulish\web-winserver>sc.exe create AspNetCore-WinServer binPath=E:/webpulish/web-winserver/WebWinServer1.exe start=auto
[SC] CreateService 成功

binPath 必须是全路径。

AspNet Core 6.0 创建windows服务的命令如下:

sc create AspNetCore-WinServer binPath= "E:\webpulish\web-winserver\WebWinServer1.exe --contentRoot E:\webpulish\web-winserver"

相较于AspNet Core 3.x、5.x,命名中多了一个参数:--contentRoot E:\webpulish\web-winserver
其中原因详见我们上面提到过的 Issues:https://github.com/dotnet/AspNetCore.Docs/issues/23387

如果你已经创建了windows服务,也可以通过如下命令修改配置:

sc config AspNetCore-WinServer binPath= "E:\webpulish\web-winserver\WebWinServer1.exe --contentRoot E:\webpulish\web-winserver"

然后执行如下命令启动windows服务:

sc start AspNetCore-WinServer

服务启动失败,这是因为,我的电脑用的是 .Net 6,现在发布的目标框架是.netcoreapp3.1
本机没有.netcore3.1的运行时。所以启动失败!

删除安装失败的服务

执行以下命令,删除服务

sc delete AspNetCore-WinServer

虽然删除WindowsServer成功,但是服务列表中还是存在,要彻底删除,
安装组合键:Win + R,输入:regedit,打开系统注册表,
删掉windows服务的注册表信息,通常路径在:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services 找到你的Service服务的名字,然后把整个文件夹删掉
这时,Windows服务列表中还是存在服务:AspNetCore-WinServer,重启电脑即可。

重新发布安装

上节说到我的电脑用的是.Net 6,现在发布的目标框架是.netcoreapp3.1 ,这种情况跟实际的生成环境一样,
生成环境有多个网站,第一个网站是 .netcore3.1, 第二个网站是基于.net5, 第三个网站是.net6,
这时不可能同时安装3个版本的 .net运行时,所以发布的时候,发布配置如下:

部署模式:选择【独立】,目标框架选择 netcoreapp3.1,如下图所示:

手把手教 net core 部署nginx .net core windows 部署_Web_03

点击发布。
复制 安装脚本 install.bat到发布目录:E:\webpulish\web-winserver 然后以管理员身份打开CMD(必须以管理员身份打开),切换到发布目录 ,执行安装脚本 install.bat

E:\webpulish\web-winserver>install.bat

输出结果:

E:\webpulish\web-winserver>set serviceName=AspNetCore-WinServer
E:\webpulish\web-winserver>set serviceFilePath=E:\webpulish\web-winserver\WebWinServer1.exe
E:\webpulish\web-winserver>set serviceDescription=AspNetCoreweb程序部署为WindowsServer
E:\webpulish\web-winserver>sc config AspNetCore-WinServer start=auto
[SC] ChangeServiceConfig 成功
E:\webpulish\web-winserver>sc description AspNetCore-WinServer AspNetCoreweb程序部署为WindowsServer
[SC] ChangeServiceConfig2 成功
E:\webpulish\web-winserver>sc start AspNetCore-WinServer
SERVICE_NAME: AspNetCore-WinServer
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 2  START_PENDING
                                (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x7d0
        PID                : 11148
        FLAGS              :
E:\webpulish\web-winserver>pause

AspNet Core 6.0 创建windows服务的命令如下:

sc create AspNetCore-WinServer binPath= "E:\webpulish\web-winserver\WebWinServer1.exe --contentRoot E:\webpulish\web-winserver"

然后执行如下命令启动windows服务:

sc start AspNetCore-WinServer

安装成功,启动也成功后,按组合键:Win + R, 输入:services.msc ,打开服务列表

手把手教 net core 部署nginx .net core windows 部署_Web_04

访问站点:http://localhost:8787/

手把手教 net core 部署nginx .net core windows 部署_Web_05

卸载服务

以管理员身份打开CMD,执行如下卸载脚本 uninstall.bat

>E:
>E:\>cd E:\webpulish\web-winserver
>E:\webpulish\web-winserver>uninstall.bat

输出结果:

E:\webpulish>set serviceName=AspNetCore-WinServer
E:\webpulish>sc stop  AspNetCore-WinServer
SERVICE_NAME: AspNetCore-WinServer
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 3  STOP_PENDING
                                (STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0

E:\webpulish>sc delete AspNetCore-WinServer
[SC] DeleteService 成功
E:\webpulish>pause
请按任意键继续. . .

或者手动输入命令执行停止和删除:

sc stop  AspNetCore-WinServer
sc delete AspNetCore-WinServer

删除成功

手把手教 net core 部署nginx .net core windows 部署_Core_06


,此时服务只是被禁用,属于标记删除,如果要彻底删除,使用注册表删除,见上一节的具体操作。

服务仅执行后台任务

创建控制台程序,项目名称为:WorkerWinService,修改项目工程文件为:

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

添加包 Microsoft.AspNetCore.Hosting.WindowsServices

<Project Sdk="Microsoft.NET.Sdk.Worker"> 
  ...
  <ItemGroup>
     ...
    <PackageReference Include="Microsoft.AspNetCore.Hosting.WindowsServices" Version="3.1.13" />
  </ItemGroup>
</Project>

接着修改 Program.cs

public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddHostedService<Worker>(); // 添加托管服务
                });

定义后台托管服务:Worker, 继承 BackgroundService Worker.cs

public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;

        public Worker(ILogger<Worker> logger)
        {
            _logger = logger;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                await Task.Delay(1000, stoppingToken);
            }
        }
    }

脚本

安装和卸载脚本同上,只是修改服务名称为 WorkerWinService 即可。

依赖部署 VS 独立部署

参见:https://docs.microsoft.com/zh-cn/aspnet/core/host-and-deploy/windows-service?view=aspnetcore-6.0&viewFallbackFrom=aspnetcore-3.0&tabs=visual-studio

总结

将 AspNet Core 部署为 Windows服务步骤:

第一步:引用包

  1. AspNet Core WebApp和 AspNet Core Web Api 添加包 Microsoft.Extensions.Hosting.WindowsServices
  2. 后台服务 Worker: BackgroundService , 添加包 Microsoft.AspNetCore.Hosting.WindowsServices

第二步: 修改 Program.cs

AspNet Core 3.x、5.x

public static IHostBuilder CreateHostBuilder(string[] args) =>
          Host.CreateDefaultBuilder(args)
              .UseWindowsService()//指定项目可以部署为Windows服务
              .ConfigureWebHostDefaults(webBuilder =>
              {
                  webBuilder.UseStartup<Startup>()
                      .UseUrls("http://*:8787"); //使用的端口号
              });

AspNet Core 6.0

var options = new WebApplicationOptions
 {
     Args = args,
     ContentRootPath = WindowsServiceHelpers.IsWindowsService() ? AppContext.BaseDirectory : default
 };

 var builder = WebApplication.CreateBuilder(options);
 builder.Host.UseWindowsService();

后台服务 Worker:BackgroundService

先定义后台托管服务:Worker, 继承 BackgroundService
Worker.cs

public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;

        public Worker(ILogger<Worker> logger)
        {
            _logger = logger;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                await Task.Delay(1000, stoppingToken);
            }
        }
    }

再添加托管服务

public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddHostedService<Worker>(); // 添加托管服务
                });

第三步:发布项目

框架目标:建议选择【独立】,但这也使得发布文件变大,根据实际情况而定吧

第四步:创建windows服务

AspNet Core 3.x、5.x 创建windows服务的命令如下:

sc create AspNetCore-WinServer binPath= "E:\webpulish\web-winserver\WebWinServer1.exe"

AspNet Core 6.0 创建windows服务的命令如下:

sc create AspNetCore-WinServer binPath= "E:\webpulish\web-winserver\WebWinServer1.exe --contentRoot E:\webpulish\web-winserver"

注意:
此时,我们并没有设置端口,默认将使用 http://localhost:5000https://localhost:5001, 执行如下命令来指定端口:

sc config AspNetCore-WinServer binPath= "E:\webpulish\web-winserver\WebWinServer1.exe --contentRoot E:\webpulish\web-winserver --urls=http://localhost:4000"

Tips:
如果创建时发现路径、参数等有误,可以执行如下命令进行修改,不用卸载windows服务:

sc config AspNetCore-WinServer binPath= "E:\webpulish\web-winserver\WebWinServer1.exe --contentRoot E:\webpulish\web-winserver"

第五步:启动 windows服务

sc start AspNetCore-WinServer

第六步:停止和删除 windows服务

sc stop AspNetCore-WinServer
sc delete AspNetCore-WinServer

执行完上面两个命令后,只是标记删除,真正删除还得到注册表中删除:
按组合键:Win + R,输入:regedit,打开系统注册表,
删掉windows服务的注册表信息,
通常路径在:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
找到你的Service服务的名字,然后把整个文件夹删掉
这时,Windows服务列表中还是存在服务:AspNetCore-WinServer,重启电脑即可。