原文:Creating Windows Services In .NET Core – Part 1 – The 「Microsoft」 Way
做者:Dotnet Core Tutorials
譯者:Lamond Lu
譯文:使用.NET Core建立Windows服務(一) - 使用官方推薦方式shell
建立Windows服務來運行批處理任務或者運行後臺任務,是一種很是常見的模式,可是因爲雲服務(Amazon Lambda, Azure WebJobs以及Azure Functions)的激增,你可能不會常用Windows服務了。我的而言,我很是喜歡使用Azure WebJobs, 由於我能夠直接編寫一個控制檯程序,而不須要考慮如何雲中運行它,一個批處理文件能夠將其裝換成一個自動化任務,而且能夠保證7*24小時的運行。c#
可是也許你尚未使用雲服務,或者你有一堆要做爲Windows服務運行的舊版應用程序須要轉換爲.NET Core, 可是不能徹底將他們轉換爲「無服務器」(serverless)應用。 那麼這邊文章就是適合你的。服務器
在許多方面,.NET Core中的Windows服務和.NET Framework中的Windows服務徹底相同。可是,在編寫服務的時候,你可能會遇到一些小問題。此外,本文中,咱們僅介紹「Microsoft」方式的Windows服務建立,在後續,我會繼續介紹如何使用第三方庫TopShelf
來簡化這該過程。app
因爲Visual Studio沒有提供建立Windows服務的模板,因此咱們須要經過建立控制檯程序的方式來建立一個Windows服務。框架
建立完成以後,咱們須要安裝一個Nuget程序包,這個程序包會將一些Windows特定的API添加到.NET Core中,這些API實際上已經在完整框架中提供了,可是其中許可能是Windows特有的,例如Windows服務。所以, 它們並無包含在.NET Core的基礎庫中,可是能夠經過將Nuget程序包的方式引入到.NET Core中。
下面咱們就能夠在Package Manager Console中輸入如下命令。less
Install-Package Microsoft.Windows.Compatibility
以上引入的Nuget程序包中,最讓咱們感興趣的是ServiceBase
類。這是一個用於編寫Windows服務的基類,它提供了一系列的事件鉤子,包含服務啓動、結束、暫停等。ide
下面呢,咱們將在代碼中建立一個類,這個類負責將一些簡單的日誌輸出到一個臨時文件中。咱們將使用這個例子來了解其中的原理。咱們的代碼以下:測試
class LoggingService : ServiceBase { private const string _logFileLocation = @"C:\temp\servicelog.txt"; private void Log(string logMessage) { Directory.CreateDirectory(Path.GetDirectoryName(_logFileLocation)); File.AppendAllText(_logFileLocation, DateTime.UtcNow.ToString() + " : " + logMessage + Environment.NewLine); } protected override void OnStart(string[] args) { Log("Starting"); base.OnStart(args); } protected override void OnStop() { Log("Stopping"); base.OnStop(); } protected override void OnPause() { Log("Pausing"); base.OnPause(); } }
因此這裏你會注意到,咱們的類是繼承了ServiceBase
類,而且咱們重寫了幾個事件方法,輸出了一些日誌。在服務啓動時,會觸發OnStart
事件,在服務終止的時候,會觸發OnStop
事件。這裏咱們不該該將過於繁重的任務放置在OnStart
事件中來處理。debug
若是咱們想從Main
方式中啓動這個服務,代碼很是的簡單。調試
static void Main(string[] args) { ServiceBase.Run(new LoggingService()); }
以上就是所有代碼。
在發佈服務的時候,咱們不可能僅依靠Visual Studio來構建咱們所須要的服務,咱們還須要專門針對Windows運行時進行構建。爲此,咱們須要在項目根目錄的命令提示符下運行如下命令。注意,這裏咱們傳入了一個-r
標記來告訴它要構建那個平臺。
dotnet publish -r win-x64 -c Release
命令運行完畢以後,咱們能夠檢查如下/bin/release/netcoreappX.X/publish
目錄,咱們能夠找到全部的發佈代碼,可是最重要的是,這裏咱們能夠獲得一個可執行的exe文件。若是咱們不指定運行時,咱們只會得到一個.NET Core的dll程序集,使用這個程序集,咱們是沒有辦法建立Windows服務的。
如今咱們能夠將這個發佈目錄移動帶其餘的任何地方,可是如今咱們就暫時使用當前的發佈目錄。
下一步,咱們須要使用管理員角色打開一個命令提示符,而後輸入一下命令。
sc create TestService BinPath=C:\full\path\to\publish\dir\WindowsServiceExample.exe
SC
命令是一個標準的Windows命令(與.NET Core無關),它能夠用來安裝Windows服務。這裏咱們將咱們的測試服務命名爲TestService
,更重要的是,咱們經過BinPath
參數指定了可執行exe文件。
運行以後,咱們應該會獲得如下結果。
[SC] CreateService SUCCESS
而後咱們要作的就是啓動服務。
sc start TestService
如今咱們能夠查看一下咱們的日誌文件,查看服務的運行狀況。
若是想要中止並刪除服務,咱們可使用一下命令。
sc stop TestService sc delete TestService
在這裏,我真的認爲,使用"Microsoft"的方式註定會失敗。由於調試服務實在是太繁瑣了。
首先,咱們將ServiceBase
中重寫的方法設置爲受保護,這意味着咱們沒法在類以外訪問它們,這使得調試它們變得更加困難。這裏我發現最好的方法是爲每一個事件提供一個public方法, 並在受保護方法中調用這些public方法來完成功能,這雖然有點混亂,
public void OnStartPublic(string[] args) { Log("Starting"); } protected override void OnStart(string[] args) { OnStartPublic(args); base.OnStart(args); }
可是至少咱們能夠作以下了事情了。
static void Main(string[] args) { var loggingService = new LoggingService(); if (true) //Some check to see if we are in debug mode (Either #IF Debug etc or an app setting) { loggingService.OnStartPublic(new string[0]); while(true) { //Just spin wait here. Thread.Sleep(1000); } //Call stop here etc. } else { ServiceBase.Run(new LoggingService()); } }
你的另外一個選擇是,在調試模式下進行項目發佈,安裝服務,而後附加調試器。實際上,這是Microsoft建議你使用的方式,可是我認爲這簡直一團糟。
實際上,咱們能夠在這裏作一些其餘很是有用的事情, 好比咱們能夠經過建立一個install.bat批處理文件來爲咱們運行SC Create命令。但我認爲,上面咱們看到的調試問題,已經讓我再也不想使用這種方式了。 幸運的是,有一個名爲Topshelf
的庫能夠幫助咱們減輕不少麻煩,在本系列的下一部分中,咱們將研究如何它。