.NET5でWindowsServiceを作成する


はじめに

.NET5が登場してからこの記事の執筆時点でもうすぐ半年が過ぎようとしています。
しかしながら、日本語での.NET5の記事があまりありませんでしたので、せっかくなので作成しようと思いました。
よろしければご参考程度にご確認ください。
また、この記事で紹介しているコードは以下のリポジトリに配置しております。

実行環境

  • VisualStudio2019
  • Windows10Pro 20H2 (Build: 19042.867)
  • .NET5 SDK 5.0.1

参考

実装

プロジェクトの作成

  1. まず、VisualStudioで新しいプロジェクトを作成します。
  2. 数多く作成できるプロジェクトはありますが、その中から WorkerService を選択します。
  3. いつも通り、プロジェクト名と作成先の場所を指定します
  4. WorkerService の作成の画面が出てきます。作成しましょう。
    1. ここでは私は.NET5を左上で選択しておきます。
    2. Dockerサポートは使用しない為、チェックがされている場合はチェックを外しておきます。
  5. 次のようにいつものソリューションエクスプローラーが表示されたなら、プロジェクトの作成に成功です。

実際のコードの実装

事前準備

コードの実装をいきなり行いたいのはやまやまですが、このままでは.NET5でのWindowsServiceは行えません。
ので、事前準備を実施します。


ソリューションエクスプローラーからプロジェクトを右クリックし、「NuGetパッケージの管理」を選択します。
開かれたNuGetの管理画面から「参照」タブを選択し、 Microsoft.Extensions.Hosting.WindowsService で検索します。
一番上にMicrosoft公式のパッケージが出てくるかと思います。そちらをインストールしてください。

次に、ソリューションエクスプローラーから Program.cs を開き、次のように、最後に .UseWindowsService() を追加してあげてください。


public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHostedService<Worker>();
            })
+           .UseWindowsService();
}

これで、WindowsServiceの一番ミニマムな実装は完了です。

せっかくなので気象庁のAPIもどきを叩いてお天気を60分に1回取ってきてみてもいいよね

この記事を書く少し前の時期に「気象庁のサイトにAPIが生えている」という話題が上がりました。
何もしないWindowsServiceも味気ないので、せっかくなのでこのAPIにアクセスしてみようと思います。
ちなみに、ここまでのミニマムな実装のままだと、WindowsServiceは1秒に1回実行する待機処理(ポーリング)をしており、
そのポーリング処理にAPIアクセスをまともに乗せると後でめちゃくちゃ怒られそうなので、1時間に1回にしようと考えます。

まずは1時間に1回の実行に変更

ポーリング処理とは言いましたが、その実態は ExecuteAsync() 内で while() があり、その中で Task.Delay() して実行間隔を制御しているにすぎません。
ですので、その Task.Delay() の間隔を変更してやればよいです。

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

気象庁APIにアクセスする

C#だと、HttpClientクラスがありますので、そちらを使用します

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
+               var client = new HttpClient();
+               var response = await client.SendAsync(new(HttpMethod.Get, @"https://www.jma.go.jp/bosai/forecast/data/forecast/130000.json"));
+               var body = await response?.Content.ReadAsStringAsync() ?? "";
+               _logger.LogInformation("response content: " + body);
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                await Task.Delay(360000, stoppingToken);
            }
        }

このように取ってきたデータが表示されていればOKです。
なお、WindowsServiceと仰々しい名前がついていますが、VisualStudioで起動したり、exeを単体実行する分には ただのコンソールアプリケーションとして扱え ますので、普通にデバッグ実行してあげるとこのようにデバッグコンソールから確認することができます。

せっかくなので、WindowsServiceへのサービス起動時処理、サービス終了時処理の生やし方も

このように生やせます。 (VisualStudioを使っているなら、 override キーワードを挿入すると勝手にサジェストされるので、そちらを確認したほうが良いかもしれませんね。)

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

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

+       public override Task StartAsync(CancellationToken cancellationToken)
+       {
+           return base.StartAsync(cancellationToken);
+       }

+       public override Task StopAsync(CancellationToken cancellationToken)
+       {
+           return base.StopAsync(cancellationToken);
+       }

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

備考

サービスの登録の方法について

アプリを配布する際にサービスへの登録が必要かと思いますが、普通、サービスの登録はユーザには操作させることはできないでしょう。 (一般ユーザーにとって、それは難易度が非常に高いものです。)
ですので、次の記事でWindowsServiceをインストール時に登録ができるインストーラーと、その使用方法を紹介したいと考えます。

おわりに

このような形で、.NET5でWindowsServiceは作成することができます。
同じ開発者の助けになる記事になることを望みます。

また、何か誤っている個所がありましたらご指摘ください。
ここまで読んでくださり、ありがとうございました。

次回