首页 文章

托管 ASP.NET Core 作为 Windows 服务

提问于
浏览
31

当我在 RC2 中获得它时,支持在 Windows 服务中托管应用程序。我试图在一个简单的 web api 项目上测试它(使用.NET Framework 4.6.1)。

这是我的 Program.cs 代码:

using System;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.WindowsServices;

namespace WebApplication4
{
  public class Program : ServiceBase
  {
    public static void Main(string[] args)
    {
      if (args.Contains("--windows-service"))
      {
        Run(new Program());
        return;
      }

      var program = new Program();
      program.OnStart(null);
      Console.ReadLine();
      program.OnStop();
    }

    protected override void OnStart(string[] args)
    {
      var host = new WebHostBuilder()
      .UseKestrel()
      .UseContentRoot(Directory.GetCurrentDirectory())      
      .UseStartup<Startup>()
      .Build();

      host.RunAsService();
    }

    protected override void OnStop() {}
  }
}

所有其他的东西基本上来自.NET 核心模板(虽然我将框架更改为 net461 并在 project.json 中添加了一些依赖项)。

dotnet publish发布它并用sc create创建 Windows 服务后,我可以成功启动我的服务,但是我无法访问任何控制器(端口没有监听)。我想我做错了什么。

所以我想主要的问题是如何制作自托管的 web api 并将其作为 Windows 服务运行。 RC2 更新后,所有找到的解决方案都不起作用。

7 回答

  • 51

    你在这里有几个选择 - 使用 Microsoft 的 WebHostService 类,继承 WebHostService 或编写自己的。后者的原因是使用 Microsoft 的实现,我们不能使用继承 WebHostService 的类型参数编写泛型扩展方法,因为此类不包含无参数构造函数,也无法访问服务定位器。

    使用 WebHostService

    在这个例子中,我将创建一个使用 Microsoft.DotNet.Web.targets 的控制台应用程序,输出.exe 并作为 MVC 应用程序(视图,控制器等)运行。我假设创建控制台应用程序,更改.xproj 中的目标并修改 project.json 以具有正确的发布选项并从标准的.NET 核心 Web 应用程序模板复制视图,控制器和 webroot - 是微不足道的。

    现在必不可少的部分:

    • 获取这个包并确保 project.json 文件中的框架是 net451(或更新版本)

    • 确保入口点中的内容根目录已正确设置为应用程序.exe 文件的发布目录,并且调用了 RunAsService()扩展方法。 E.g:

    public static void Main(string[] args)
    {
        var exePath= System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
        var directoryPath = Path.GetDirectoryName(exePath);
    
        var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(directoryPath)
            .UseStartup<Startup>()
            .Build();
    
        if (Debugger.IsAttached || args.Contains("--debug"))
        {
            host.Run();
        }
        else
        {
            host.RunAsService();
        }
    }
    

    现在可以使用以下命令轻松安装.exe

    sc create MyService binPath = "Full\Path\To\The\Console\file.exe"
    

    启动服务后,将托管 Web 应用程序并成功查找并呈现其视图。

    继承 WebHostService

    这种方法的一个主要好处是,它允许我们覆盖 OnStopping,OnStarting 和 OnStarted 方法。

    我们假设以下是继承 WebHostService 的自定义类

    internal class CustomWebHostService : WebHostService
    {
        public CustomWebHostService(IWebHost host) : base(host)
        {
        }
    
        protected override void OnStarting(string[] args)
        {
            // Log
            base.OnStarting(args);
        }
    
        protected override void OnStarted()
        {
            // More log
            base.OnStarted();
        }
    
        protected override void OnStopping()
        {
            // Even more log
            base.OnStopping();
        }
    }
    

    按照上面的步骤,我们必须写下我们自己的扩展方法,使用我们的自定义类运行主机:

    public static class CustomWebHostWindowsServiceExtensions
    {
        public static void RunAsCustomService(this IWebHost host)
        {
            var webHostService = new CustomWebHostService(host);
            ServiceBase.Run(webHostService);
        }
    }
    

    从前一个入口点示例中唯一要改变的行是最后的 else 语句,它必须调用正确的扩展方法

    host.RunAsCustomService();
    

    最后,使用与上面相同的步骤安装服务。

    sc create MyService binPath = "Full\Path\To\The\Console\file.exe"
    
  • 9

    如果可以仅在完整的.NET Framework 上运行,那么之前的答案就足够了(继承自ServiceBase或使用 Microsoft 的WebHostService)。

    但是,如果你想/只想运行**.NET 核心**(在 windows nano 服务器上 e.g. ,在 Linux 上作为普通应用程序和 Windows 上的服务工作的单个二进制文件,或者需要在应用程序上运行 side-by-side 旧的.NET 框架版本,所以你无法更新系统框架),你必须通过 P/Invokes 自己做适当的 Windows API 调用。因为我必须这样做,所以我创造了一个这样做的库

    当使用管理权限(e.g. dotnet MyService.dll --register-as-service)运行时,有一个样品可用,也可以自行安装。

  • 5

    您需要引用Microsoft.AspNetCore.HostingMicrosoft.AspNetCore.Hosting.WindowsServices并在 Startup 类中使用此代码使其作为 Windows 服务运行:

    public static void Main(string[] args)
    {
        var host = new WebHostBuilder()
                    .UseIISIntegration()
                    .UseKestrel()
                    .UseContentRoot(@"Path\To\Content\Root")
                    .UseStartup<Startup>()
                    .Build();
    
        if (Debugger.IsAttached || args.Contains("--debug"))
        {
            host.Run();
        }
        else
        {
            host.RunAsService();
        }
    }
    

    然后你需要为IWebHost.RunAsService添加一个扩展方法,用你的自定义WebHostService类包装OnStoppingOnStarting事件处理程序:

    public static class MyWebHostServiceServiceExtensions
    {
        public static void RunAsMyService(this IWebHost host)
        {
            var webHostService = new MyWebHostService(host);
            ServiceBase.Run(webHostService);
        }
    }
    
    internal class MyWebHostService : WebHostService
    {
        public MyWebHostService(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();
        }
    }
    

    这篇文章如何在 Windows 服务中托管 ASP.NET Core和评论涵盖了细节。

    更新(详见快速总结 ASP.NET 核心 2.0 中的新功能):

    ASP.NET Core 1.1我们有这样的事情:

    public class Program
    {
        public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseKestrel()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseStartup<Startup>()
                .Build();
    
            host.Run();
        }
    }
    

    ASP.NET Core 2.0中,它看起来像这样:

    public class Program
    {
        public static void Main(string[] args)
        {
            BuildWebHost(args).Run();
        }
    
        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .Build();
    }
    
  • 2

    我在寻找类似问题的解决方案时找到了这个主题。结束实现非常(非常)简化 Topshelf 的工作。您可以轻松地作为控制台运行 and/or 安装,卸载等一项服务。我认为这对某些人来说非常有用。你可以在这里找到代码:https://github.com/PeterKottas/DotNetCore.WindowsService这里还有 nuget 和https://www.nuget.org/packages/PeterKottas.DotNetCore.WindowsService/。享受:)

  • 2

    使用 Visual Studio 2017 可以轻松创建 Windows 服务。您需要将.NET Core 代码移动到可以引用 NetStandard 的类库。您的 Windows 服务项目必须以完整的.NET Framework 为目标,但可以引用您的共享库。

    通过将所有代码放在类库中,您就可以在 Windows 服务和其他非 Windows 应用程序之间进行移植。

    使用 VS 2015,您无法创建 Windows 服务项目并引用.NET Core xproj。 Visual Studio 2017 修复了将所有项目转换为 csproj 后的问题。

    这篇博客更详细地介绍了它:如何使用 Visual Studio 2017 创建.NET 核心 Windows 服务

  • 1

    使用最新的 VS2015 Update 2 ASP.NET 核心 Web 应用程序(.NET 框架)模板和下面的 Program.cs 模块,它作为服务运行时正常工作,stand-alone 和 VS.请注意,作为服务运行时,需要设置包含 wwwroot 的目录的路径。

    到目前为止,我真的很喜欢这个模板和模式。可以像往常一样在 Visual Studio 中开发 MVC6 代码,然后将其干净地部署为服务。实际上,我注意到在部署服务的本地实例进行测试时,不需要使用 sc.exe 进行 delete/create 服务。只需停止服务,发布代码并重新启动服务,它就会获取新的更改。

    public class Program
    {
        public static void Main(string[] args)
        {
            IWebHost host = new WebHostBuilder()
                .UseKestrel()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseStartup<Startup>()
                .Build();
    
            if (args.Contains("--windows-service"))
            {
                host = new WebHostBuilder()
                .UseKestrel()
                .UseContentRoot("<directory-containing-wwwroot>")
                .UseIISIntegration()
                .UseStartup<Startup>()
                .UseUrls("http://+:5000")
                .Build();
    
                Startup.is_service = true;
                host.RunAsService();
            }
            else
            {
                host.Run();
            }
        }
    }
    
  • 1

    更新了我用于.netcore 2.1 的内容,使用了尽可能多的库存模板,其中包括从 appsettings.json,命令参数,主机过滤等中读取配置,这是在.netcore 团队的辅助方法中实现的。因此,许多这些设置通过配置文件或命令 args 进行控制。查看 microsoft 的文档,了解他提供的库存模板以及如何覆盖设置。

    请注意,如果您使用 https 和默认开发证书定义端点,我看到该服务无法启动。可能只是指定 pfx via 配置。如果有人想要改进这一点,请随意这样做。

    无论如何这里是我在 program.cs 中使用的股票模板代码

    public class Program
    {
        public static void Main(string[] args)
        {
    
            if (args.Contains("--service"))
            {
                var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                Directory.SetCurrentDirectory(path); // need this because WebHost.CreateDefaultBuilder queries current directory to look for config files and content root folder. service start up sets this to win32's folder.
                var host = CreateWebHostBuilder(args).Build();
                host.RunAsService();
            }
            else
            {
                CreateWebHostBuilder(args).Build().Run();
            }
    
        }
    
        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
    }
    

相关问题