ASP.NET Core の DI のコードを理解する

Azure Functions は、中身を追うと、ASP.NET Core や DI がらみの知識が必要になってくる。 

最新の Azure Functions Host に対して、Durable Function のサンプルアプリを実行すると実行できるのに、なぜか、`Microsoft.Azure.WebJobs.Extensions.Storage` を足すとなぜか`IHostingEnvironment` が Storage Extension 側の DI の設定パートの箇所で取得できなくなっていたが、原因はわからなかった。IHostingEnvironmnet を `Startup.cs` の `ConfigServices` の箇所でチェックすると特に問題は起きてないので、その後の Extensions のロードと、Startup の部分、もしくはライブラリバージョンの問題が起きたのかもしれない。

だから、IHostingEnvironment がどこでセットされているのかを知りたかった。まずは下記のコードをしっかり読み解こう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/4af6c79a-df96-aee0-fc75-94a6c81a7469.png)

この書き方は [option pattern](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#bind-hierarchical-configuration-data-using-the-options-pattern)と呼ばれる書き方で、次のような Option クラスを定義する。

# Option クラスの定義

```csharp
    public class MyOptions
    {
        public const string My = "My";

        public string Key { get; set; }
        public string Value { get; set; }
    }
```

# コンフィグレーションの取得

`appsettings.json` に対して次のような定義をしておく。先ほどの Option クラスの構造だ。

_appsettings.json_

```json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "My": {
    "Key": "key1",
    "Value":  "value1"
  }
}

```
  
次のコードで、コンフィグレーションから、`MyOptions.My` つまり、`My` の要素の `appsettings.json` を読み込み、そのインスタンスを、`ServiceCollection` に登録している。

```csharp
services.Configure<MyOptions>(Configuration.GetSection(MyOptions.My));
```

# コールバックの登録

`AddOptions` で、Option のビルダを返却し、`Configure` メソッドで `Action`

```csharp
services.AddOptions<MyOptions>()
    .Configure<Microsoft.AspNetCore.Hosting.IHostingEnvironment>((options, env) =>
    {
        options.Value = "value2";
    });
```

`Configure` の説明は、`Registers an action used to configure a particular type of options. Note: These are run before all Microsoft.Extensions.Options.OptionsBuilder`1.PostConfigure(System.Action{`0}).` であり、正直いまいちよくわからない。

この Action はいつ実行されるのであろうか？上記のコードを [ASP.NET Core Sample](https://github.com/TsuyoshiUshio/ASPNETCoreSpike) を作ってみて試してみる。

ブレークポイントを置いてみると、起動しても、このActionは実行されない。となると、Lazy で実行されるはずなので、コントローラーで使ってみる。テンプレートに下記を追加する。

_WeatherForcastController.cs_

```csharp

        private readonly IOptions<MyOptions> _options;

        public WeatherForecastController(ILogger<WeatherForecastController> logger, IOptions<MyOptions> options)
        {
            _logger = logger;
            _options = options;
        }
        [HttpGet]
        public IEnumerable<WeatherForecast> Get()
        {
            var rng = new Random();
            Console.WriteLine($"Config Value: {_options.Value.Value}");
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            })
            .ToArray();
        }
```
予想通り、コントローラーでオプションを使うように設定すると、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/d906fe3e-c46c-8cf3-6486-eb4f35e26008.png)

該当箇所にヒットするので、レイジーで、コンフィグレーションを書き換えるような使い方が出来る。よって、最初の例では、Development Environmnet の時の MaxPollingInterval を書き換える設定にしているわけである。[デフォルトは１分](https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-queue-output?tabs=csharp#hostjson-settings)なので、デバック用にキューが早く Consume される設定になってるわけですな。

つまり、上記の問題は、コンフィグを使用するときに呼ばれるものなので、そもそも、`IHostingEnvrionment` のインスタンスが null なのは、よくわからないわけになる。より深く調べてみると、ServiceType がちょっと変である。ここは、本来 `IHostingEnvironment` になるはず。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/cc2f5548-e8cd-3d96-9300-ddbb972495ea.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/20c18905-d2f5-4e5a-c84a-20effae0ffe3.png)
	Name	Value	Type
▶	ServiceType	{Name = Inspecting the state of an object in the debuggee of type System.Type is not supported in this context. FullName = Inspecting the state of an object in the debuggee of type System.Type is not supported in this context.}	System.Type {System.RuntimeType}


# 解決

とてもくだらない理由だった。 Azure Functions Host のリポジトリをクローンして使っているが、なぜか `dev` ブランチ (1/17/2021) だとそうなるようだ。こうすれば問題はなくなるが、学びにはなったので、ブログにしておく。折角なので、[Azure Functions Host](https://github.com/Azure/azure-functions-host) をクローンして、デバッグするときの仕方を書いておく。

```bash
$ git clone git@github.com:Azure/azure-functions-host.git
$ git checkout refs/tags/v3.0.15149   // version should be changed.
```

on your functions project

```bash
$ dotnet publish
```

Open the Visual Studio. Right click `WebJobs.Script.WebHost`. Debug > Environment Variables. Configure the value like this.

|Name	|Value |
| ----- | -------|
|WEBSITE_SITE_NAME| ushio |
|AzureWebJobsScriptRoot| YOUR_PUBLISH_DIRECTORY (the directory has host.json after publish) |
|ASPNETCORE_ENVIRONMENT| Development |
|AzureWebJobsStorage| UseDevelopmentStorage=true |
|FUNCTIONS_WORKER_RUNTIME|dotnet|
|AZURE_FUNCTIONS_ENVIRONMENT| Development|

そして、F5 そうすると、自分がターゲットにしている Azure Functions Host バージョンでデバッグをすることが出来る。リプロ環境の場合は、完全に同じバージョンにすることがとても大切だ。そうでないと私のように時間を無駄にしてしまうのですw