.NETの汎用ホストの公式Docをやさしくしました


公式ドキュメントが難しかったので、それを易しく解説していきます。

https://docs.microsoft.com/ja-jp/dotnet/core/extensions/generic-host#host-configuration

テンプレートからコンソールアプリを作成し、そこにホストを実装します。
汎用ホストとは何でしょうか？
公式ドキュメントによると以下のようにあります。
>''ホスト'' とは、次のようなアプリのリソースと有効期間機能をカプセル化するオブジェクトです。
>
>[依存関係の挿入 (DI)](#4-作成したサービスをホストさせる)
>[ログの記録](#8-ログプロバイダーを変更する)
>[構成](#6-appsettingsjsonの設定をホストに反映する)
>[アプリのシャットダウン](#9-ホストにアプリをシャットダウンさせる)
>[IHostedService の実装](#4-作成したサービスをホストさせる)

これだけではあまりピンとこない方もいると思うので、デフォルトの設定のホストをコンソールアプリに実装して実行した出力例をご覧ください。

```bash
bash-3.2$ ./ConsoleApp
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /Users/ken/repos/ConsoleApp

#----- Ctrl + Cを押すと終了 --------

^Cinfo: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
```

デフォルトではCtrl+Cで強制停止するまでアプリは起動し続け、特に何もすることはありません。ここに任意の処理を差し込んだり、それが終わったらアプリを終了させる処理をさせたりして目的のアプリを作っていきます。

2章ではホストを構築するためのプロジェクトの作成した、3章から本題に入ります。
4章以降で、公式ドキュメントが説明する　「IHostedService の実装」等の機能を順次解説していきます。


## 1. 必要なツール

### 1.1. 開発ツール


アプリを作成するために必要なツールになります。

|ツール|用途|インストール先|メモ|
|-----|-|----------|-----|
|.NET SDK|アプリのビルド|https://docs.microsoft.com/ja-jp/dotnet/core/install/windows?tabs=net60 <br> 筆者の開発環境はM1 Macなのでうまくいくのか心配でしたが、[brew cask](https://formulae.brew.sh/cask/dotnet-sdk)でインストールできました。(2022/8/29)|汎用ホストのアプリは以下の.NET系の環境でも動きますが、それらを統合した.NETで作っておけば間違い無いでしょう。執筆時の[LTSバージョン](https://docs.microsoft.com/ja-jp/lifecycle/products/microsoft-net-and-net-core)は6でした。<ul><li>.NET Framwark</li><li>.NET Core</li><li></li>Xamarin</ul>|
|テキストエディタ|コーディング|お好きのものを。<br>迷ったら[VS Code](https://azure.microsoft.com/ja-jp/products/visual-studio-code/)|筆者はemacsです|

### 1.2 実行環境

ビルドしたアプリをデプロイするマシンに以下をインストールします。

|ツール|用途|インストール先|メモ|
|-----|-|----------|-----|
|.NETランタイム|アプリの実行|SDKのバージョンと同じものをインストール。<br>https://docs.microsoft.com/ja-jp/dotnet/core/install/windows?tabs=net60|以下の場合は必要ありません。<ul><li>.NETランタイム無しでも実行できるアプリにビルドする場合([2.2章参照](#22-(任意)単独で動くバイナリに設定する))。</li><li></li>開発環境と同じバージョンのSDKがインストールされている場合</ul>|




## 2. 準備

### 2.1. テンプレートからコンソールアプリのプロジェクトを作成

任意のシェルのコマンドラインから、[コンソールアプリのテンプレート](https://docs.microsoft.com/ja-jp/dotnet/core/tools/dotnet-new-sdk-templates)を選択してプロジェクトを作成します。

```bash
dotnet new console -o ConsoleApp
``` 

ConsoleAppはプロジェクト名です。お好きな名前で結構です。
デフォルトでは実行ファイルはこの名前で作成されます。

以下の構造のディレクトリが作成されます。

```bash
ConsoleApp
├── ConsoleApp.csproj
├── Program.cs
└── obj
```



### 2.2. ビルドアーティファクトのパッケージング

ビルドしたファイルを1つの実行ファイルにパッケージングする設定です。

#### 2.2.1. プロジェクトファイルの変更

以下を参照してプロジェクトファイル(ConsoleApp.csproj)を変更します。

https://docs.microsoft.com/ja-jp/dotnet/core/deploying/single-file/overview?tabs=cli

```diff_xml:ConsoleApp.csproj
  <Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
      <OutputType>Exe</OutputType>
      <TargetFramework>net6.0</TargetFramework>
      <ImplicitUsings>enable</ImplicitUsings>
      <Nullable>enable</Nullable>
+     <PublishSingleFile>true</PublishSingleFile>
+     <SelfContained>true</SelfContained>
+     <DebugType>embedded</DebugType>
    </PropertyGroup>

  </Project>

```

`SelfContained`をtrueにすればその中に.NETランタイムも含めることができて、.NETランタイムをインストールしていないマシンでも実行できるようになります。必要ない場合はfalseに。(ちょっと違う気がしますが、サーブレットコンテナとアーティファクトをまとめてパッケージングしたjarのような感じ(spring boot))

#### 2.2.2. ビルドと実行

アプリをビルドして単一のファイルになるか確認してみます。
プロジェクトのルートディレクトリで、以下のコマンドを実行します。

```bash
dotnet publish -r <ターゲットとする OS と CPU の種類> -o <ビルドアーティファクトの出力先>
```

-r の引数には、ターゲットとする OS と CPU の種類を指定します。
x64CPUで動くWindows OSのマシンの場合は、win-x64です。

https://docs.microsoft.com/ja-jp/dotnet/core/rid-catalog



以下M1 Macで動く実行ファイルにビルドして、アプリを実行するまでの例です。

```shell
bash-3.2$ dotnet publish -r osx.12-arm64 -o dist
MSBuild version 17.3.0+92e077650 for .NET
  復元対象のプロジェクトを決定しています...
  復元対象のすべてのプロジェクトは最新です。
  ConsoleApp -> /Users/ken/repos/ConsoleApp/bin/Debug/net6.0/osx.12-arm64/ConsoleApp.dll
  ConsoleApp -> /Users/ken/repos/ConsoleApp/dist/
bash-3.2$ ./dist/ConsoleApp
Hello, World!
```


### 2.3 パッケージマネジャの設定をする

nuget.configを作成します。
```
ConsoleApp
├── ConsoleApp.csproj
├── nuget.config     <--- 新規作成
├── Program.cs
├── bin
├── dist
│   └── ConsoleApp.exe
├── obj
└── packages
```


```xml:nuget.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <config>
        <add key="globalPackagesFolder" value="./packages" />
    </config>
    <packageRestore>
        <add key="enabled" value="True" />
        <add key="automatic" value="True" />
    </packageRestore>
    <packageSources>
        <add key="NuGet official package source" value="https://api.nuget.org/v3/index.json" />
    </packageSources>
    <packageManagement>
        <add key="format" value="1" />
        <add key="disabled" value="False" />
    </packageManagement>
</configuration>
```

以下の設定をしました。

- globalPackagesFolder:　nugetが依存パッケージをダウンロードするディレクトリを指定。筆者はグローバルの領域を汚したくないためこうしています。
- packageRestore:　ビルド時に依存パッケージが上記で指定したディレクトリになければ、ダウンロードするように設定します。
- packageSources:　パッケージリポジトリを指定します。
- packageManagement:　プロジェクトで使用するパッケージの指定を、[プロジェクトファイル(csproj拡張子のファイル)のPackageReference](#31-依存パッケージを解決する)でするように設定します

https://docs.microsoft.com/ja-jp/nuget/reference/nuget-config-file


## 3. デフォルトの設定のホストを起動するアプリを作成

コンソールアプリでデフォルトの設定のホストを起動させます。
このページの冒頭で紹介した出力になるアプリを作成します。

テンプレートから作成したコンソールアプリから変更するのは、以下2つのファイルです。

- ConsoleApp.csproj
- Program.cs


### 3.1. 依存パッケージを解決する

ホスト構築に使うクラスなどは名前空間:Microsoft.Extensions.Hostingで定義されており、これを含むパッケージ:[Microsoft.Extensions.Hosting](https://www.nuget.org/packages/Microsoft.Extensions.Hosting/6.0.1)をプロジェクトにインポートする必要があります。

PackageReferenceで、パッケージ名とバージョンを指定します。

```diff_xml:ConsoleApp.csproj
   　　</PropertyGroup>

+  　　<ItemGroup>
+   　　 <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
+  　　</ItemGroup>

 　　　</Project>
```

### 3.2. デフォルトのビルダーでホストを作成する

Program.csを以下に置き換えます。

```C#:Program.cs
using Microsoft.Extensions.Hosting;

//Step1 ビルダーを作成
IHostBuilder builder = Host.CreateDefaultBuilder();

//Step2 ホストをビルド
IHost host = builder.Build();

//Step3 ホストを起動
host.Run();
```

※ [5以上のバージョンの.NET](https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/configure-language-version)のC#では[mainメソッドが省略できる](https://docs.microsoft.com/ja-jp/dotnet/csharp/fundamentals/program-structure/top-level-statements)になったようなので、活用しています。

ホストのオブジェクト([IHostインターフェース](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.extensions.hosting.ihost?view=dotnet-plat-ext-6.0)実装)は、[ビルダーのオブジェクトから生成](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.extensions.hosting.ihostbuilder.build?view=dotnet-plat-ext-6.0#microsoft-extensions-hosting-ihostbuilder-build)します。
ホストに実行される処理は、ホストをビルドする前のビルダーから追加設定していきます。([4章](#4-作成したサービスをホストさせる)で解説)



## 4. 作成したサービスをホストさせる

ホストに実行させる処理を記述するコンポーネントを`サービス`と言います。
[デフォルトのビルダーでビルドしたホストもすでにサービスをホストしており](https://docs.microsoft.com/ja-jp/dotnet/core/extensions/dependency-injection#framework-provided-services)、3章の紹介したアプリを実現しています。
本章ではサービスを新規作成し、ホストにそれを追加でホストさせます。


### 4.1. サービスの作成

```
ConsoleApp                     <--カレントディレクトリ
├── ConsoleApp.csproj
├── ExampleHostedService.cs    <-----　新規作成
├── nuget.config
├── Program.cs
├── bin
├── dist
│   └── ConsoleApp.exe
├── obj
└── packages
```

ExampleHostedService.csにサービスを記述します。

1. 名前空間：Test.Servicesを定義
2. 1の名前空間に、クラス:ExampleHostedServiceを定義
      - 実装するインターフェース: [Microsoft.Extensions.Hosting.IHostedServiceインターフェース](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.extensions.hosting.ihostedservice?view=dotnet-plat-ext-6.0)
3. 2のクラスに、実装したインターフェースの以下のメソッドを記述
   - [StartAsync](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.extensions.hosting.ihostedservice.startasync?view=dotnet-plat-ext-6.0#microsoft-extensions-hosting-ihostedservice-startasync(system-threading-cancellationtoken))
   - [stopAsync](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.extensions.hosting.ihostedservice.stopasync?view=dotnet-plat-ext-6.0#microsoft-extensions-hosting-ihostedservice-stopasync(system-threading-cancellationtoken))


```c#:ExampleHostedService.cs
using Microsoft.Extensions.Hosting;

//クラスを定義する名前空間
namespace Test.Services;

//サービスのクラス
public sealed class ExampleHostedService : IHostedService
{
    // サービスが開始するときにトリガーされるメソッド
    public Task StartAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("StartAsync has been called.");
        return Task.CompletedTask;
    }

    // サービスが終了するときにトリガーされるメソッド
    public Task StopAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("StopAsync has been called.");
        return Task.CompletedTask;
    }
}
```

[Microsoftの公式の汎用ホストの説明](https://docs.microsoft.com/ja-jp/dotnet/core/extensions/generic-host)の以下がクリアになったのではないでしょうか。
> ホストが起動すると、サービス コンテナーのホステッド サービスのコレクションに登録されている IHostedService の各実装で IHostedService.StartAsync が呼び出されます。 


公式の解説では、別途定義した他のメソッドもトリガされるようにしていますね。そのために、インターフェース: [Microsoft.Extensions.Hosting.IHostApplicationLifetime](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.extensions.hosting.ihostapplicationlifetime?view=dotnet-plat-ext-6.0)をインジェクションして、対応するプロパティにそれらのメソッドを登録しています。その他にも、文字の出力にロガーを使うなどちゃんとしてますね。本章の説明では、複雑になるので割愛しました。



### 4.2. ホストにサービスを追加する

ここで新たに使用する[Microsoft.Extensions.DependencyInjection.IServiceCollectionインターフェース](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.extensions.dependencyinjection.iservicecollection?view=dotnet-plat-ext-6.0)のパッケージは、3.1章でインポートした[Microsoft.Extensions.Hostingのパッケージの依存関係](https://www.nuget.org/packages/Microsoft.Extensions.Hosting/6.0.1#dependencies-body-tab)にあるので、すでにインポートされています。明示的にPackageReferenceに追加する必要はありません。

```diff_c_sharp:Program.cs
  using Microsoft.Extensions.Hosting;
+ using Microsoft.Extensions.DependencyInjection; //IServiceCollectionインターフェースを定義する名前空間
+ using Test.Services;  //4.1章で作成したクラスの名前空間

  IHostBuilder builder = Host.CreateDefaultBuilder();

  //ビルダーを通して、サービスコンテナ(サービスを格納するIoCコンテナ)に追加のサービスを登録します。
+ builder = builder.ConfigureServices(delegate(HostBuilderContext hostBuilderContext, IServiceCollection serviceCollection){ AddServiceToHost(hostBuilderContext,  serviceCollection); } );

  IHost host = builder.Build();
  host.Run();

  //4　ビルダーのConfigureServicesメソッドにデリゲートを使って渡すメソッド(任意の名前)
+ static IServiceCollection AddServiceToHost(HostBuilderContext hostBuilderContext, IServiceCollection ServiceCollection){
+     return ServiceCollection.AddHostedService<ExampleHostedService>();
+ }
```




これをメソッドチェーンを使って必要ない変数を省略し、[デリゲートをラムダ式で記述](https://docs.microsoft.com/ja-jp/dotnet/api/system.action-2?view=net-6.0&viewFallbackFrom=dotnet-plat-ext-6.0)すると、[公式Docによる解説](https://docs.microsoft.com/ja-jp/dotnet/core/extensions/generic-host#set-up-a-host)のようにかっこよくワンライナーで書けます。

```c#:Program.cs
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Test.Services;

Host.CreateDefaultBuilder()
    .ConfigureServices((hostBuilderContext,serviceCollection) => serviceCollection.AddHostedService<ExampleHostedService>())
    .Build()
    .Run();
```

アプリを再ビルドして起動するとサービスが開始と終了中に[4.1章](#41-サービスの作成)で設定した文字列が出力されます。

```bash
bash-3.2$ dotnet publish -r osx.12-arm64 -o dist
MSBuild version 17.3.0+92e077650 for .NET
  復元対象のプロジェクトを決定しています...
  復元対象のすべてのプロジェクトは最新です。
  ConsoleApp -> /Users/ken/repos/ConsoleApp/bin/Debug/net6.0/osx.12-arm64/ConsoleApp.dll
  ConsoleApp -> /Users/ken/repos/ConsoleApp/dist/
bash-3.2$ ./dist/ConsoleApp
 StartAsync has been called.
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /Users/ken/repos/ConsoleApp
^Cinfo: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
StopAsync has been called.
```


## 5. バックグラウンドタスクのサービスをホストさせる


本章ではバックグラウンドタスクを定義してサービスコンテナに登録します。
筆者は[このようなケース](https://qiita.com/matsumon-development/items/783ed157238ed3b1a56b)で使いました。

### 5.1. サービスの作成

```
ConsoleApp                               <--カレントディレクトリ
├── ConsoleApp.csproj
├── ExampleBackgroundHostedService.cs    <-----　新規作成
├── ExampleHostedService.cs
├── nuget.config
├── Program.cs
├── bin
├── dist
│   └── ConsoleApp.exe
├── obj
└── packages
```

ExampleBackgroundHostedService.csにサービスを記述します。

1. 名前空間：Test.Servicesを定義
2. 1の名前空間に、クラス:ExampleBackgroundHostedServiceを定義
      - 継承クラス: [Microsoft.Extensions.Hosting.BackgroundServiceクラス](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.extensions.hosting.backgroundservice?view=dotnet-plat-ext-6.0)
3. 2のクラスで、継承したクラスのExecuteAsyncメソッドをオーバーライド



```csharp:ExampleBackgroundHostedService.cs
using Microsoft.Extensions.Hosting;

namespace Test.Services;

public sealed class ExampleBackgroundHostedService : BackgroundService
{
    
    //BackgroundServiceのExecuteAsyncメソッドは、オーバーライドが必須。サービスが開始するときにトリガーされる
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            Console.WriteLine(DateTimeOffset.Now);
            await Task.Delay(1000, stoppingToken);
        }
    }
}
```

起動したアプリにCtrl+Cをインプットすると、シャットダウンをトリガーするトークンがサービスに送られます。
それを受信するまで、1秒ごとに時間を出力するプログラムです。


### 5.2. 作成したサービスをホストさせる

```diff_c_sharp:Program.cs
  using Microsoft.Extensions.Hosting;
  using Microsoft.Extensions.DependencyInjection;
  using Test.Services;

+ await
  Host.CreateDefaultBuilder()
      .ConfigureServices((hostBuilderContext,serviceCollection) => serviceCollection.AddHostedService<ExampleHostedService>())
+     .ConfigureServices((hostBuilderContext,serviceCollection) => serviceCollection.AddHostedService<ExampleBackgroundHostedService>())
      .Build()
-     .Run();
+     .RunAsync();
```

- 4.2章のサービスに加えて、5.1章で作成したサービスをサービスコンテナに登録します。
- 新たに登録したサービスは非同期処理をするので、ホストはメソッド:[RunAsync](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.extensions.hosting.hostingabstractionshostextensions.runasync?view=dotnet-plat-ext-6.0#microsoft-extensions-hosting-hostingabstractionshostextensions-runasync(microsoft-extensions-hosting-ihost-system-threading-cancellationtoken))で起動します

再ビルドしてアプリを起動すると以下の出力となります。

```bash
bash-3.2$ dotnet publish -r osx.12-arm64 -o dist
MSBuild version 17.3.0+92e077650 for .NET
  復元対象のプロジェクトを決定しています...
  復元対象のすべてのプロジェクトは最新です。
  ConsoleApp -> /Users/ken/repos/ConsoleApp/bin/Debug/net6.0/osx.12-arm64/ConsoleApp.dll
  ConsoleApp -> /Users/ken/repos/ConsoleApp/dist/
bash-3.2$ ./dist/ConsoleApp
 StartAsync has been called.
2022/08/31 19:48:43 +09:00
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /Users/ken/repos/ConsoleApp
2022/08/31 19:48:44 +09:00
2022/08/31 19:48:45 +09:00
2022/08/31 19:48:46 +09:00
2022/08/31 19:48:47 +09:00
^Cinfo: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
StopAsync has been called.
```

### 5.3. worker テンプレート

[2章](#21-テンプレートからコンソールアプリのプロジェクトを作成)ではコンソールアプリのテンプレートを使ってプロジェクトを作成しましたが、Workerアプリのテンプレートも用意されています。このテンプレートでプロジェクトを作成することにより、5章までの構成で始めることができます。

```
dotnet new worker -o "プロジェクトを展開するディレクトリパス"
```

https://learn.microsoft.com/ja-jp/dotnet/core/extensions/workers

なおこのテンプレートで作ったプロジェクトでは、Microsoft.Extensions.HostingやMicrosoft.Extensions.DependencyInjection等の名前空間を明示的にusingディレクティブで書かなくても、その名前空間以下のクラス等にパスが通りました。
[暗黙的に宣言](https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/using-directive#global-modifier)されているようです。



## 6. appsettings.jsonの設定をホストに反映する

[3章](#32-デフォルトのビルダーでホストを作成する)以降はデフォルトの構成でビルダーを作成しました。
予め設定した内容でビルダーを作成する方法の1つは、プロジェクトのルートディレクトリに配置したappsettings.jsonに設定を記述することです。

https://docs.microsoft.com/ja-jp/dotnet/core/extensions/generic-host#default-builder-settings

```
ConsoleApp                               <--カレントディレクトリ
├── appsettings.json                      <-----　新規作成
├── ConsoleApp.csproj
├── ExampleBackgroundHostedService.cs
├── ExampleHostedService.cs
├── nuget.config
├── Program.cs
├── bin
├── dist
│   └── ConsoleApp.exe
├── obj
└── packages
```

### 6.1 任意のkey-value形式のパラメータを渡す


#### 6.1.1 appsettings.jsonへの設定


```json:appsettings.json
{
  "Parameters": {
    "param1":  "りんご",
    "param2":  "ゴリラ",
    "param3":  "ラッパ"
  }
}
```


#### 6.1.2 設定したパラメータを参照する

サービスでappsettings.jsonで指定したパラメータを参照するようにします。

ここで新たに使用する以下2つのインターフェースのパッケージは、3.1章でインポートした[Microsoft.Extensions.Hostingのパッケージの依存関係](https://www.nuget.org/packages/Microsoft.Extensions.Hosting/6.0.1#dependencies-body-tab)にあるので、すでにインポートされています。明示的に[PackageReference](#31-依存パッケージを解決する)に追加する必要はありません。

- [Microsoft.Extensions.Configuration.IConfiguration](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.extensions.configuration.iconfiguration?view=dotnet-plat-ext-6.0)
- [Microsoft.Extensions.Configuration.IConfigurationSection](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.extensions.configuration.iconfigurationsection?view=dotnet-plat-ext-6.0)


```diff_c_sharp:ExampleHostedService.cs
  using Microsoft.Extensions.Hosting;
+ using Microsoft.Extensions.Configuration;
  
  namespace Test.Services;
  
  public sealed class ExampleHostedService : IHostedService{

+    private readonly IConfiguration _configuration;

     //IConfigurationインターフェースをコンストラクタでインジェクションする
     //ここにappsettings.jsonで指定したパラメータが入っている
+    public  ExampleHostedService(IConfiguration configuration){
+       _configuration = configuration;
+    }
     public Task StartAsync(CancellationToken cancellationToken)
     {
        Console.WriteLine("StartAsync has been called.");

        //パラメータを参照。jsonのセクションを指定します。
+       IConfigurationSection parameters = _configuration.GetSection("Parameters");
+       Console.WriteLine(parameters.GetValue<String>("param1"));
+       Console.WriteLine(parameters.GetValue<String>("param2"));
+       Console.WriteLine(parameters.GetValue<String>("param3"));
        return Task.CompletedTask;
     }
  
      public Task StopAsync(CancellationToken cancellationToken){
         Console.WriteLine("StopAsync has been called.");
         return Task.CompletedTask;
      }
  }
```


本筋からはそれますが、コンソールの出力のうち5章で作成したバックグラウンドタスクのサービスのものが邪魔なので無効にしておきます。

```diff_c_sharp:Program.cs
  using Microsoft.Extensions.Hosting;
  using Microsoft.Extensions.DependencyInjection;
  using Test.Services;

  await
  Host.CreateDefaultBuilder()
      .ConfigureServices((hostBuilderContext,serviceCollection) => serviceCollection.AddHostedService<ExampleHostedService>())
-     .ConfigureServices((hostBuilderContext,serviceCollection) => serviceCollection.AddHostedService<ExampleBackgroundHostedService>())
      .Build()
      .RunAsync();
```

以下再ビルドしてアプリを実行したときの出力です。

```bash
bash-3.2$ dotnet publish -r osx.12-arm64 -o dist
MSBuild version 17.3.0+92e077650 for .NET
  復元対象のプロジェクトを決定しています...
  復元対象のすべてのプロジェクトは最新です。
  ConsoleApp -> /Users/ken/repos/ConsoleApp/bin/Debug/net6.0/osx.12-arm64/ConsoleApp.dll
  ConsoleApp -> /Users/ken/repos/ConsoleApp/dist/
bash-3.2$ ./dist/ConsoleApp
StartAsync has been called.
りんご
ゴリラ
ラッパ
2022/08/31 20:00:43 +09:00
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /Users/ken/repos/ConsoleApp
^Cinfo: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
StopAsync has been called.
```


### 6.2 デフォルトのロガーの設定を変更する

5章までのアプリの実行結果からコンソールに出力されたログは、infoレベルのみです。
デフォルト設定のホストのロガでinfoレベル以上を出力する設定なのか、debugレベルのログがないのか?


#### 6.2.1 サービスにデバッグログを追加する

Debugレベルのログをサービスに追加します。


ここで新たに使用する以下のインターフェースのパッケージは、3.1章でインポートした[Microsoft.Extensions.Hostingのパッケージの依存関係](https://www.nuget.org/packages/Microsoft.Extensions.Hosting/6.0.1#dependencies-body-tab)にあるので、すでにインポートされています。明示的に[PackageReference](#31-依存パッケージを解決する)に追加する必要はありません。

- [Microsoft.Extensions.Logging.ILogger](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.extensions.logging.ilogger?view=dotnet-plat-ext-6.0)



```diff_c_sharp:ExampleHostedService.cs
  using Microsoft.Extensions.Hosting;
  using Microsoft.Extensions.Configuration;
+ using Microsoft.Extensions.Logging;
  
  namespace Test.Services;
  
  public sealed class ExampleHostedService : IHostedService{

      private readonly IConfiguration _configuration;

-     public  ExampleHostedService(IConfiguration configuration){
      //ILoggerインターフェースをコンストラクでインジェクションする
+     public  ExampleHostedService(IConfiguration configuration, ILogger<ExampleHostedService> logger){
          _configuration = configuration;
+         _logger = logger;
      }

      public Task StartAsync(CancellationToken cancellationToken){
-         Console.WriteLine("StartAsync has been called.");
          //上記の出力をログとしての出力に変更する。
+         _logger.LogDebug("StartAsync has been called.");
          IConfigurationSection parameters = _configuration.GetSection("Parameters");
          Console.WriteLine(parameters.GetValue<String>("param1"));
          Console.WriteLine(parameters.GetValue<String>("param2"));
          Console.WriteLine(parameters.GetValue<String>("param3"));
          return Task.CompletedTask;
     }
  
      public Task StopAsync(CancellationToken cancellationToken){
-         Console.WriteLine("StopAsync has been called.");
          //上記の出力をログとしての出力に変更する。
+         _logger.LogDebug("StopAsync has been called.");
          return Task.CompletedTask;
      }
  }
```



アプリを再ビルドして、実行してみます。

```bash
bash-3.2$ dotnet publish -r osx.12-arm64 -o dist
MSBuild version 17.3.0+92e077650 for .NET
  復元対象のプロジェクトを決定しています...
  復元対象のすべてのプロジェクトは最新です。
  ConsoleApp -> /Users/ken/repos/ConsoleApp/bin/Debug/net6.0/osx.12-arm64/ConsoleApp.dll
  ConsoleApp -> /Users/ken/repos/ConsoleApp/dist/
bash-3.2$ ./dist/ConsoleApp
りんご
ゴリラ
ラッパ
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /Users/ken/repos/ConsoleApp
^Cinfo: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
```

おや、、追加したdebugレベルのログがコンソールに出力されませんね。
デフォルトの設定のホストのロガーは、出力をinfo以上のログに制限していたようです。

#### 6.2.2 ロガーの設定を変更する 

設定されているログプロバイダーに定義されているすべてのロガーの出力レベルをDebug以上に変更します。

```diff_json:appsettings.json
  {
+   "Logging": {
+     "LogLevel": {
+       "Default": "Debug",
+     }
+   },
    "Parameters": {
      "param1":  "りんご",
      "param2":  "ゴリラ",
      "param3":  "ラッパ"
    }
  }
```


アプリを再ビルドして起動すると、コンソールへDebugレベルのログが出力されます。
デフォルトの設定のホストにはDebugレベルのログが無いわけではなかったようですね。
6.1章で書いたものに加えて、`Microsoft.Extensions.Hosting.Internal.Host`のdebugログが確認できます。

```bash
bash-3.2$ dotnet publish -r osx.12-arm64 -o dist
MSBuild version 17.3.0+92e077650 for .NET
  復元対象のプロジェクトを決定しています...
  復元対象のすべてのプロジェクトは最新です。
  ConsoleApp -> /Users/ken/repos/ConsoleApp/bin/Debug/net6.0/osx.12-arm64/ConsoleApp.dll
  ConsoleApp -> /Users/ken/repos/ConsoleApp/dist/
bash-3.2$ ./dist/ConsoleApp
dbug: Microsoft.Extensions.Hosting.Internal.Host[1]
      Hosting starting
dbug: Test.Services.ExampleHostedService[0]
      StartAsync has been called.
りんご
ゴリラ
ラッパ
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /Users/ken/repos/ConsoleApp
dbug: Microsoft.Extensions.Hosting.Internal.Host[2]
      Hosting started
^Cinfo: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
dbug: Microsoft.Extensions.Hosting.Internal.Host[3]
      Hosting stopping
dbug: Test.Services.ExampleHostedService[0]
      StopAsync has been called.
dbug: Microsoft.Extensions.Hosting.Internal.Host[4]
      Hosting stopped
```





## 7. 任意のjsonファイルの設定をホストに反映する

6章ではappsettings.jsonにホストへの設定を記述しましたが、追加で設定ファイルを読み込ませることもできます。

### 7.1 任意のjsonファイルの設定をホストに反映する

任意の名前で設定ファイルを追加します。

```
ConsoleApp                                <--カレントディレクトリ
├── appsettings.json
├── appsetting2.json                      <-----　新規作成
├── ConsoleApp.csproj
├── ExampleBackgroundHostedService.cs
├── ExampleHostedService.cs
├── nuget.config
├── Program.cs
├── bin
├── dist
│   └── ConsoleApp.exe
├── obj
└── packages
```


```json:appsetting2.json
{
  "Parameters": {
    "param3":  "ライオン"
  }
}
```

ホストに作成した追加の設定ファイルを読み込ませます。

```diff_c_sharp:Program.cs
  using Microsoft.Extensions.Hosting;
  using Microsoft.Extensions.DependencyInjection;
+ using Microsoft.Extensions.Configuration;
  using Test.Services;

  await
  Host.CreateDefaultBuilder()
+     .ConfigureAppConfiguration((hostContext, config) =>
+     {
+          config.AddJsonFile("appsetting2.json", optional: true);
+     })
      .ConfigureServices(( hostBuilderContext,serviceCollection) =>  serviceCollection.AddHostedService<ExampleHostedService>())
      .Build()
      .RunAsync();
```

再ビルドしてアプリを実行してみます。
ラッパがライオンに変わりました。appsettings.jsonの内容が上書きされています。


```bash
bash-3.2$ dotnet publish -r osx.12-arm64 -o dist
MSBuild version 17.3.0+92e077650 for .NET
  復元対象のプロジェクトを決定しています...
  復元対象のすべてのプロジェクトは最新です。
  ConsoleApp -> /Users/ken/repos/ConsoleApp/bin/Debug/net6.0/osx.12-arm64/ConsoleApp.dll
  ConsoleApp -> /Users/ken/repos/ConsoleApp/dist/
bash-3.2$ ./dist/ConsoleApp
dbug: Microsoft.Extensions.Hosting.Internal.Host[1]
      Hosting starting
dbug: Test.Services.ExampleHostedService[0]
      StartAsync has been called.
りんご
ゴリラ
ライオン
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /Users/ken/repos/ConsoleApp
dbug: Microsoft.Extensions.Hosting.Internal.Host[2]
      Hosting started
^Cinfo: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
dbug: Microsoft.Extensions.Hosting.Internal.Host[3]
      Hosting stopping
dbug: Test.Services.ExampleHostedService[0]
      StopAsync has been called.
dbug: Microsoft.Extensions.Hosting.Internal.Host[4]
      Hosting stopped
```




### 7.2 jsonファイルの配置先を変更する

appsetting.jsonなどのホストの設定ファイルは、デフォルトのホストではアプリを起動したユーザーのカレントディレクトリのものが読まれるようになっています。

ディレクトリを移動すると、appsetting.json等が読み込まれず6章以前の設定に戻っていしまいます。

```bash
bash-3.2$ cd dist/
bash-3.2$ pwd
/Users/ken/repos/ConsoleApp/dist
bash-3.2$ ./ConsoleApp
bash-3.2$ ./ConsoleApp



info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /Users/ken/repos/ConsoleApp/dist
^Cinfo: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
```

空行はappsettings.jsonが読み込まれないがため、[ExampleHostedService.cs](#612-設定したパラメータを参照する)でnullを出力しているためです。

実行ファイルと同じディレクトリに配置したものが読まれるようにしておくと便利なのではないでしょうか？このように配置して運用することが多いと思われます。

```powershell
PS C:\ProgramData\ConsoleApp> dir

   ディレクトリ: C:\ProgramData\ConsoleApp

Mode       LastWriteTime     Length    Name
------     -------------     ------    ----
-a----     2022/09/01 14:00  69228308  ConsoleApp.exe
-a----     2022/09/01 14:00  69228308  appsettings.json
```



ホストに設定ファイルの配置先を設定します。

```diff_c_sharp:Program.cs
  using Microsoft.Extensions.Hosting;
  using Microsoft.Extensions.DependencyInjection;
  using Microsoft.Extensions.Configuration;
  using Test.Services;
+ using System.Diagnostics;

  await
  Host.CreateDefaultBuilder()
      .ConfigureAppConfiguration((hostContext, config) =>
      {
+          config.SetBasePath(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName));
           config.AddJsonFile("appsetting2.json", optional: true);
      })
      .ConfigureServices(( hostBuilderContext,serviceCollection) =>  serviceCollection.AddHostedService<ExampleHostedService>())
      .Build()
      .RunAsync();
```

設定ファイルを移動します。

```
ConsoleApp                    
├── ConsoleApp.csproj
├── ExampleBackgroundHostedService.cs
├── ExampleHostedService.cs
├── nuget.config
├── Program.cs
├── bin
├── dist
│   ├── appsettings.json   <--移動
│   ├── appsetting2.json   <--移動
│   └── ConsoleApp.exe
├── obj
└── packages
```

何処のディレクトリでアプリを実行しても7章と同じ出力になります。


## 8. ログプロバイダーを変更する

今までのアプリのコンソール出力を見てきたとおり、ホストにはすでに[組み込みのログプロバイダ](https://docs.microsoft.com/ja-jp/dotnet/core/extensions/logging-providers#built-in-logging-providers)があります。しかし、このログプロバイダはファイルに出力できません。このままでは使いにくいので変更します。

[こちらに](https://docs.microsoft.com/ja-jp/dotnet/core/extensions/logging-providers#third-party-logging-providers)サードパーティのログプロバイダがいくつか紹介されていました。
このうちのNLogの実装を紹介していきます。


### 8.1 デフォルトのログプロバイダを無効にする

組み込みのログプロバイダーが必要ない場合は無効にします。

```diff_c_sharp:Program.cs
  using Microsoft.Extensions.Hosting;
  using Microsoft.Extensions.DependencyInjection;
  using Microsoft.Extensions.Configuration;
+ using Microsoft.Extensions.Logging;
  using Test.Services;
  using System.Diagnostics;

  await
  Host.CreateDefaultBuilder()
      .ConfigureAppConfiguration((hostContext, config) =>
      {
           config.SetBasePath(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName));
           config.AddJsonFile("appsetting2.json", optional: true);
      })
+     .ConfigureLogging((hostCotext,logging) =>
+     {
+          logging.ClearProviders();
+     } )
      .ConfigureServices(( hostBuilderContext,serviceCollection) =>  serviceCollection.AddHostedService<ExampleHostedService>())
      .Build()
      .RunAsync();
```

再ビルドしてアプリを実行すると、コンソールにログがすべて出力されなくなっています。

```bash
bash-3.2$ dotnet publish -r osx.12-arm64 -o dist
MSBuild version 17.3.0+92e077650 for .NET
  復元対象のプロジェクトを決定しています...
  復元対象のすべてのプロジェクトは最新です。
  ConsoleApp -> /Users/ken/repos/ConsoleApp/bin/Debug/net6.0/osx.12-arm64/ConsoleApp.dll
  ConsoleApp -> /Users/ken/repos/ConsoleApp/dist/
bash-3.2$ ./dist/ConsoleApp
りんご
ゴリラ
ライオン
^C
```

### 8.2. [NLog](https://nlog-project.org/)をホストのログプロバイダに設定する

#### 8.2.1. NLogの設定ファイルを作成する

以下の出力を設定します。

- すべてのロガーのInfoレベル以上のログをコンソールに出力
- すべてのロガーのDebugレベル以上のログを指定したファイルに出力

https://github.com/NLog/NLog.Extensions.Logging/wiki/NLog-configuration-with-appsettings.json

nlog.jsonを作成します。

```
ConsoleApp                    
├── ConsoleApp.csproj
├── ExampleBackgroundHostedService.cs
├── ExampleHostedService.cs
├── nuget.config
├── Program.cs
├── bin
├── dist
│   ├── appsettings.json        
│   ├── appsetting2.json　　　　　　　　　　　　　　　　
│   ├── nlog.json　　　　　　　　　　　　　　　　<--新規作成
│   └── ConsoleApp.exe
├── obj
└── packages
```

```json:nlog.json
{
  "NLog": {
    "throwConfigExceptions": true,

    "targets": {
      "logconsole": {
        "type": "Console"
      },
      "logfile": {
        "type": "File",
        "fileName": "./log/ConsoleApp_${shortdate}.log"
      }
    },

    "rules": [
      {
        "logger": "*",
        "minLevel": "Info",
        "writeTo": "logconsole"
      },
      {
        "logger": "*",
        "minLevel": "debug",
        "writeTo": "logfile"
      }
    ]
  }
}
```

#### 8.2.2. 依存パッケージを解決する

Nlogのクラスなどは名前空間:NLog.Extensions.Loggingで定義されており、これを含むパッケージ:[NLog.Extensions.Logging](https://www.nuget.org/packages/NLog.Extensions.Logging)をプロジェクトにインポートする必要があります。

PackageReferenceでパッケージ名とバージョンを指定して、インポートします。

```diff_xml:ConsoleApp.csproj
   　　</PropertyGroup>

   　　<ItemGroup>
        <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
+   　　   <PackageReference Include="NLog.Extensions.Logging" Version="5.0.4" />
   　　</ItemGroup>

 　　　</Project>
```


#### 8.2.3 ホストにNLogの設定ファイルを読み込ませる

Program.csを変更します。

```diff_c_sharp:Program.cs
  using Microsoft.Extensions.Hosting;
  using Microsoft.Extensions.DependencyInjection;
  using Microsoft.Extensions.Configuration;
  using Microsoft.Extensions.Logging;
+ using NLog.Extensions.Logging;
  using Test.Services;
  using System.Diagnostics;

  await
  Host.CreateDefaultBuilder()
      .ConfigureAppConfiguration((hostContext, config) =>
      {
           config.SetBasePath(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName));
           config.AddJsonFile("appsetting2.json", optional: true)
           //ホストの設定にnlog.jsonを追加します
+          config.AddJsonFile("nlog.json", optional: true);
      })
      .ConfigureLogging((hostCotext,logging) =>
      {
           logging.ClearProviders();
           //ホストが起動したとき、設定ファイルのNLogセクションの内容を指定してNLogを初期化します
+          logging.AddNLog(new NLogLoggingConfiguration(hostCotext.Configuration.GetSection("NLog")));
      } )
      .ConfigureServices(( hostBuilderContext,serviceCollection) =>  serviceCollection.AddHostedService<ExampleHostedService>())
      .Build()
      .RunAsync();
```

- クラス: [NLog.Extensions.Logging.NLogLoggingConfiguration](https://nlog-project.org/documentation/v4.7.0/html/T_NLog_Extensions_Logging_NLogLoggingConfiguration.htm)
- メソッド: [NLog.Extensions.Logging.AddNLog](https://nlog-project.org/documentation/v4.5.0/html/M_NLog_Extensions_Logging_ConfigureExtensions_AddNLog.htm)


本章での内容でアプリを再ビルドして、実行してみます。
コンソールへの出力です。


```bash
bash-3.2$ dotnet publish -r osx.12-arm64 -o dist
MSBuild version 17.3.0+92e077650 for .NET
  復元対象のプロジェクトを決定しています...
  復元対象のすべてのプロジェクトは最新です。
  ConsoleApp -> /Users/ken/repos/ConsoleApp/bin/Debug/net6.0/osx.12-arm64/ConsoleApp.dll
  ConsoleApp -> /Users/ken/repos/ConsoleApp/dist/
bash-3.2$ ./dist/ConsoleApp
りんご
ゴリラ
ライオン
2022-09-02 14:14:45.9419|INFO|Microsoft.Hosting.Lifetime|Application started. Press Ctrl+C to shut down.
2022-09-02 14:14:45.9453|INFO|Microsoft.Hosting.Lifetime|Hosting environment: Production
2022-09-02 14:14:45.9461|INFO|Microsoft.Hosting.Lifetime|Content root path: /Users/ken/repos/ConsoleApp
^C2022-09-02 14:14:47.3169|INFO|Microsoft.Hosting.Lifetime|Application is shutting down...
```

ログファイルへの出力です。

```bash
2022-09-02 14:14:45.6962|DEBUG|Microsoft.Extensions.Hosting.Internal.Host|Hosting starting
2022-09-02 14:14:45.9371|DEBUG|Test.Services.ExampleHostedService|StartAsync has been called.
2022-09-02 14:14:45.9419|INFO|Microsoft.Hosting.Lifetime|Application started. Press Ctrl+C to shut down.
2022-09-02 14:14:45.9453|INFO|Microsoft.Hosting.Lifetime|Hosting environment: Production
2022-09-02 14:14:45.9461|INFO|Microsoft.Hosting.Lifetime|Content root path: /Users/ken/repos/ConsoleApp
2022-09-02 14:14:45.9461|DEBUG|Microsoft.Extensions.Hosting.Internal.Host|Hosting started
2022-09-02 14:14:47.3169|INFO|Microsoft.Hosting.Lifetime|Application is shutting down...
2022-09-02 14:14:47.3220|DEBUG|Microsoft.Extensions.Hosting.Internal.Host|Hosting stopping
2022-09-02 14:14:47.3220|DEBUG|Test.Services.ExampleHostedService|StopAsync has been called.
2022-09-02 14:14:47.3225|DEBUG|Microsoft.Extensions.Hosting.Internal.Host|Hosting stopped
```


## 9. ホストにアプリをシャットダウンさせる


8章まではユーザーによる入力(Ctrl+C)でアプリを終了させましたが、本章ではホストにアプリを終了させます。5章で作成したバックグラウンドタスクのサービスを、10回のループした後アプリをシャットダウンする処理にします。


ExampleBackgroundHostedService.csを変更します。

```diff_c_sharp:ExampleBackgroundHostedService.cs
  using Microsoft.Extensions.Hosting;

  namespace Test.Services;

  public sealed class ExampleBackgroundHostedService : BackgroundService{
+     private readonly IHostApplicationLifetime _applicationLifetime;

      //アプリをシャットダウンさせるメソッドを持つ、IHostApplicationLifetimeインターフェースをコンストラクタインジェクションします。
+     public ExampleBackgroundHostedService(IHostApplicationLifetime applicationLifetime){
+         _applicationLifetime = applicationLifetime;
+     }

      protected override async Task ExecuteAsync(CancellationToken stoppingToken){
          while (!stoppingToken.IsCancellationRequested){
-             Console.WriteLine(DateTimeOffset.Now);
-             await Task.Delay(1000, stoppingToken);
+             for(int i = 1; i <= 10; i++){
+                 Console.WriteLine(i);
+                 if(i == 10){
                      //アプリをシャットダウンさせます         
+                     _applicationLifetime.StopApplication();
+                 }
+                 await Task.Delay(1000, stoppingToken);
+             }
          }
      }
  }

```


7章で上記サービスは無効にしていたので、ホストにサービスを追加し直します。

Program.csを変更します

```diff_c_sharp:Program.cs
  using Microsoft.Extensions.Hosting;
  using Microsoft.Extensions.DependencyInjection;
  using Microsoft.Extensions.Configuration;
  using Microsoft.Extensions.Logging;
  using NLog.Extensions.Logging;
  using Test.Services;
  using System.Diagnostics;

  await
  Host.CreateDefaultBuilder()
      .ConfigureAppConfiguration((hostContext, config) =>
      {
           config.SetBasePath(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName));
           config.AddJsonFile("appsetting2.json", optional: true)
           config.AddJsonFile("nlog.json", optional: true);
      })
      .ConfigureLogging((hostCotext,logging) =>
      {
           logging.ClearProviders();
           logging.AddNLog(new NLogLoggingConfiguration(hostCotext.Configuration.GetSection("NLog")));
      } )
-     .ConfigureServices(( hostBuilderContext,serviceCollection) =>  serviceCollection.AddHostedService<ExampleHostedService>())
+     .ConfigureServices(( hostBuilderContext,serviceCollection) =>  serviceCollection.AddHostedService<ExampleBackgroundHostedService>())
      .Build()
      .RunAsync();
```


アプリを再ビルドして実行します。
10秒たつとCtrl+Cを入力せずとも、アプリがシャットダウンします。

```bash
bash-3.2$ dotnet publish -r osx.12-arm64 -o dist
MSBuild version 17.3.0+92e077650 for .NET
  復元対象のプロジェクトを決定しています...
  復元対象のすべてのプロジェクトは最新です。
  ConsoleApp -> /Users/ken/repos/ConsoleApp/bin/Debug/net6.0/osx.12-arm64/ConsoleApp.dll
  ConsoleApp -> /Users/ken/repos/ConsoleApp/dist/
bash-3.2$ ./dist/ConsoleApp
1
2022-09-02 20:33:20.3657|INFO|Microsoft.Hosting.Lifetime|Application started. Press Ctrl+C to shut down.
2022-09-02 20:33:20.3693|INFO|Microsoft.Hosting.Lifetime|Hosting environment: Production
2022-09-02 20:33:20.3693|INFO|Microsoft.Hosting.Lifetime|Content root path: /Users/ken/repos/ConsoleApp
2
3
4
5
6
7
8
9
10
2022-09-02 20:33:29.3946|INFO|Microsoft.Hosting.Lifetime|Application is shutting down...
```


## 10. Widowsサービスとして起動できるようにする

### 10.1. 依存パッケージを解決する

[Microsoft.Extensions.Hosting.WindowsServices](https://www.nuget.org/packages/Microsoft.Extensions.Hosting.WindowsServices)のパッケージが必要になります。

PackageReferenceに追加します。

```diff_xml:ConsoleApp.csproj
   　　</PropertyGroup>

   　　<ItemGroup>
        <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
+   　　   <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
    　　   <PackageReference Include="NLog.Extensions.Logging" Version="5.0.4" />
   　　</ItemGroup>

 　　　</Project>
```


### 10.2. ホストへの設定

Program.csを変更します

```diff_c_sharp:Program.cs
  //UseWindowsServiceメソッドのために別途パッケージをインストールしたが、IHostBuilderインターフェースの拡張メソッドなのでこの名前空間には含まれる
  using Microsoft.Extensions.Hosting;
  using Microsoft.Extensions.DependencyInjection;
  using Microsoft.Extensions.Configuration;
  using Microsoft.Extensions.Logging;
  using NLog.Extensions.Logging;
  using Test.Services;
  using System.Diagnostics;

  await
  Host.CreateDefaultBuilder()
      .ConfigureAppConfiguration((hostContext, config) =>
      {
           config.SetBasePath(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName));
           config.AddJsonFile("appsetting2.json", optional: true)
           config.AddJsonFile("nlog.json", optional: true);
      })
      .ConfigureLogging((hostCotext,logging) =>
      {
           logging.ClearProviders();
           logging.AddNLog(new NLogLoggingConfiguration(hostCotext.Configuration.GetSection("NLog")));
      } )
      .ConfigureServices(( hostBuilderContext,serviceCollection) =>  serviceCollection.AddHostedService<ExampleBackgroundHostedService>())
+     .UseWindowsService() //Windowsサービスとして利用するための設定
      .Build()
      .RunAsync();
```