前言

今年初因為專案因素,有機會了解如何製作一個 WebAPI  (MVC架構) 的 Windows Services,加上近幾年幾乎都在開發 ASP.NET C# MVC 相關程式,對於這種簡易服務製作印象相當深刻。最近無意間發現 ASP.NET Core 也能製作這種類型的服務,且有別於原先 topshelf + console app 的方式,ASP.NET Core 本身架構與特色使開發過程更加輕鬆與簡便。本篇內容如下:

建立 Windows Services
    建立 ASP.NET Core Web 應用程式(.Net Framework)
    透過 Nuget 安裝 Microsoft.AspNetCore.Hosting.WindowsServices
    修改應用程式執行路徑與執行方式
    發佈專案至資料夾
    透過 SC 指令建立 Windows Services
進階說明
    偵錯與除錯
    優點
    修改 URL

本篇文章參考官方文件並簡單介紹個人實作過程,若有任何建議或錯誤請各位先進不吝指教,謝謝。



介紹

建立 ASP.NET Core Web 應用程式(.Net Framework) 

1. 開啟 Visual Studio Community 2017 -> 檔案 -> 新增 ->專案


2. 選擇ASP.NET Core Web 應用程式 (.NET Framework)

Step 3. 我們選擇 Web API 範本,點選確定完成專案建立


透過 Nuget 安裝 Microsoft.AspNetCore.Hosting.WindowsServices

Step 1. 我們選擇 工具 -> NuGet封裝管理員 -> 管理方案的 Nuget 套件

Step 2. 於瀏覽輸入 Microsoft.AspNetCore.Hosting.WindowsServices 搜尋並進行安裝




修改應用程式執行路徑與執行方式


我們需要稍微修改一下 ASP.NET Core Web 應用程式:
1. 應用程式取得靜態資料夾位址的方法為 Directory.GetCurrentDirectory();但若是建構 Windows Services,我們取得位置為該服務執行的資料夾。
2. 原先我們需要透過 host.Run() 啟動我們的應用程式;但若是建構 Windows Services,我們執行的方法為 host.RunAsService()。

    public class Program
    {
        public static void Main(string[] args)
        {
            var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
            var pathToContentRoot = Path.GetDirectoryName(pathToExe);
            var host = new WebHostBuilder()
                .UseKestrel()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseContentRoot(pathToContentRoot)
                .UseIISIntegration()
                .UseStartup()
                .UseApplicationInsights()
                .Build();
            host.Run();
            host.RunAsService();
        }
    }



發佈專案至資料夾

Step 1.對專案點選右鍵 -> 發行

Step 2.選擇資料夾,我們將路徑更改為 C:\WindowsServicExample,選擇發行


Step 3. 於資料夾可以看見發行程式內容



SC 指令建立 windows service

我們能透過 SC 指令建立、開始、停止予刪除服務,語法如下:
sc create MyService binPath="C:\WindowsServiceExample\AspNetCoreWindowsServices.exe"
sc start MyService
sc stop MyService
sc delete MyService
MyService 為服務名稱,可自行命名,binPath為程式所在位置。
2019.04.14 因應後續 .NET Core 改版,目前設定服務部分改由 powershell 方式,您可以參考在 Windows 服務上裝載 ASP.NET Core

Step 1.我們先建立與啟動服務

Step 2.開啟服務檢視MyService

Step 3.開啟網頁,輸入http://localhost:5000/api/values,可以看見 WebAPI 正常啟動

若需要重新安裝服務,可以透過 stop與 delete 指令移除服務


除錯與測試

當我們在開發windows services 必須不斷重新安裝、啟動,工作相當繁複,對於除錯、測試與讀取 log 相當不方便。我們其實先轉回開發 console app 方式進行開發,待告一段落後再註冊服務進行實際測試。下面參考官方網站改寫program.cs,讓您於開發期間以 console app方式啟動,而發佈後以windows service 方式執行。
public static void Main(string[] args)
{
    bool isService = true;
    if (Debugger.IsAttached || args.Contains("--console"))
    {
        isService = false;
    }
    var pathToContentRoot = Directory.GetCurrentDirectory();
    if (isService)
    {
        var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
        pathToContentRoot = Path.GetDirectoryName(pathToExe);
    }


    var host = new WebHostBuilder()
    .UseKestrel()
    .UseContentRoot(pathToContentRoot)
    .UseIISIntegration()
    .UseStartup()
    .UseApplicationInsights()
    .Build();
    if (isService)
    {

        host.RunAsService();
    }
    else
    {
        host.Run();
    }
}



優點

如前言所描述,有別於過去 TopShelf 方式製作此類型的服務必須安裝 OWIN、DI相關套件、 撰寫部分程式。而透過 ASP.NET Core + .NET Framework的方式,這些步驟都省略了。
注:雖然外殼是ASP.NET Core,但實際使用是 .NET Framework,使用套件方式與原先撰寫.NET Framework相同。



設定 Url

當您完成你的服務,如果想要設定 URL,可以依據需求加上:
UseUrls("http://*:5000;http://www.windowsservicetest.com;https://10.10.10.10")




參考資料

1. Host an ASP.NET Core app in a Windows Service - Microsoft docs
2. SC - Microsoft TechNet