ASP.NET Core の AppSetting の構成方法

ASP.NET が技術検証で必要になって、最近いろんなツールが、.Net Core ベースに移行しているので.NET Core の方を使ってみることにした。Core になったからといって、大した違いはないだろう、、、と思っていたけどいきなり、AppSettings 周りのアーキテクチャが変更になっていてがっつり躓いた。従来は、Configuration Manager だったのだが、こいつが消滅している。インターネットを検索しても、現在のASP.NET Core のテンプレートと異なり混乱したので、自分のために整理しておく。

# ASP.NET Core + AppSettings でやりたかったこと

凄く単純なことで、普通にAzure上だと WebApp　の AppSettings からKey/Valueの値をとってきて、ローカル開発しているときは、ローカルのファイルから取ってきたい。という単純な事

# Configuration Builder 

AppSettings 周りを取得するためには、[ConfigurationBuilder](https://github.com/aspnet/Configuration/blob/dc6fbde39f532bf154ee1eb02d8be08a0a1605e4/src/Config/ConfigurationBuilder.cs) を使う。試用イメージを見てもらうほうがわかりやすいだろう。

```csharp
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
        }
```

こんな感じでコードを書くと`appsettings.json`, `appsettings.Development/Staging/Production.json` それと環境変数から、値を取得できるようになる。WebAppの AppSettingsの項目は、環境変数になるので、このコードを書いておけばローカルは、`appsettings.json`もしくは、`appsettings.環境名.json` のファイルに置いておいて、WebAppはいつもの、AppSettings に書けばいいということになる。ちなみに、`appsettings.環境名.json` の環境名は、`ASPNETCORE_ENVIRONMENT` で指定する。詳しくはこちらを参照

* [Working with multiple environments](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments)

そして、そのあとは便利なメソッドがあるので、クラスを作っておくと、そこにそれらの値をバインドしてくれるようになる。実際わたしが作成したコードを見てみよう。

# ASP.NET Core MVC のコンフィグ例

このあたりの情報は世の中に出回っている内容は若干古いようです。例えばよくあるのが、上記のコードをStartupに記述するというのがありますが、必要ありません。Startup.cs のコードを読んでみると、既にProgram.cs の方で上記のコードが実装済みらしいです。

_Program.cs_

```csharp
        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .Build();
    }
```

WebHostのコードを読むとコメントに書いてあります。

_WebHost_

```csharp
        // Summary:
        //     Initializes a new instance of the Microsoft.AspNetCore.Hosting.WebHostBuilder
        //     class with pre-configured defaults.
        //
        // Parameters:
        //   args:
        //     The command line args.
        //
        // Returns:
        //     The initialized Microsoft.AspNetCore.Hosting.IWebHostBuilder.
        //
        // Remarks:
        //     The following defaults are applied to the returned Microsoft.AspNetCore.Hosting.WebHostBuilder:
        //     use Kestrel as the web server, set the Microsoft.AspNetCore.Hosting.IHostingEnvironment.ContentRootPath
        //     to the result of System.IO.Directory.GetCurrentDirectory, load Microsoft.Extensions.Configuration.IConfiguration
        //     from 'appsettings.json' and 'appsettings.[Microsoft.AspNetCore.Hosting.IHostingEnvironment.EnvironmentName].json',
        //     load Microsoft.Extensions.Configuration.IConfiguration from User Secrets when
        //     Microsoft.AspNetCore.Hosting.IHostingEnvironment.EnvironmentName is 'Development'
        //     using the entry assembly, load Microsoft.Extensions.Configuration.IConfiguration
        //     from environment variables, load Microsoft.Extensions.Configuration.IConfiguration
        //     from supplied command line args, configures the Microsoft.Extensions.Logging.ILoggerFactory
        //     to log to the console and debug output, enables IIS integration, enables the
        //     ability for frameworks to bind their options to their default configuration sections,
        //     and adds the developer exception page when Microsoft.AspNetCore.Hosting.IHostingEnvironment.EnvironmentName
        //     is 'Development'
        public static IWebHostBuilder CreateDefaultBuilder(string[] args);
```

ですので、あなたが必要なことは、まずコンフィグのデータを保持するためのモデルを書くことです。

#　appsettings.json の例

```json
{
  "CosmosDB": {
    "EndPointUrl": "https://xxxxx.documents.azure.com:443/",
    "AuthorizationKey": "SOMESECRET"
  }
}
```

このデータを読み込むための設定をします。

# Model の作成

`appsettings.json` の構造に合わせてクラスを作ります。

_AppSettings.cs_

```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace PrintServerlessWeb.Models
{
    public class AppSettings
    {
        public CosmosDB CosmosDB { get; set; }
    }

    public class CosmosDB
    {
        public string EndPointUrl { get; set; }
        public string AuthorizationKey { get; set; }
    }
}
```

# Startup.cs

一部を書き換えます。`services.Configure` に下記のようなコードを書くだけで、先ほどのモデルクラスにコンフィグをロードしてくれます。他に、特定の階層を指定してコードを書く方法もあります。興味がある人は[次のページ]((https://stackoverflow.com/questions/43448993/asp-net-core-mvc-app-settings))でサンプルを見てください。これは便利ですね。ちなみにASP.NET Coreではなくて、自分で素で書くときも、指定したクラスにロードしてくれるメソッドがあるので、これは便利ですね。ちなみに、services のインスタンスに足しておくと、Dependency Injection してくれて、コントローラーから取得できるようになります。

_Startup.cs_

```csharp
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.Configure<AppSettings>(Configuration);
        }
```

# HomeController.cs

コントローラー側に、自動で Dependency Injection してくれるので、そのコードを追加します。そしたら使いたい放題です。

_HomeController.cs_

```csharp
        private readonly AppSettings appSettings;

        public HomeController(IOptions<AppSettings> optionsAccessor)
        {
            this.appSettings = optionsAccessor.Value;
        }

     :
        public IActionResult Index()
        {
            Console.WriteLine("***************** start");
            Console.WriteLine($"CosmosDB: EndPointUrl : {this.appSettings.CosmosDB.EndPointUrl}");
            Console.WriteLine($"CosmosDB: AuthorizationKey: {this.appSettings.CosmosDB.AuthorizationKey}");
            Console.WriteLine("***************** end");
            return View();
        }
     :
```

ちなみに、DI は次のパッケージで出来るようになっているみたい。

* [DependencyInjection](https://github.com/aspnet/DependencyInjection)

# Web App の AppSettings はどうなるか？

WebApp のコンフィグはどのようになるかというと、AppSettings の項目に

`CosmosDB:EndPointUrl` といったjsonファイルの設定の構造に合わせてた書き方をします。すると、同じインターフェイスで読めるようになります。
　これは、当初かなり違和感がありました。なんで、jsonファイルの方に合わせないといけないのか？と思いました。しかしよくよく考えると、コネクションストリングとかは、例えば、CosmosDBのコネクションストリングだったり、KeyVaultのコネクションストリングだったりするわけです。そうなると、`COSMOSDB_CONNECTION_STRING` とか、`KEY_VAULT_CONNECTION_STRING` になるわけで、結局のところ、`CosmosDB:ConnectionString` と意味的に大差ありません。しかもコードの方は明確に環境変数の構造化ができるので、こっちのほうがいい気がしてきました。

これでしっかりと設定ができます。

# Resource

* [Working with Azure App Services Application Settings and Connection Strings in ASP.NET Core](https://blogs.msdn.microsoft.com/cjaliaga/2016/08/10/working-with-azure-app-services-application-settings-and-connection-strings-in-asp-net-core/) <- 決定版
* [ASP.NET Core MVC App Settings](https://stackoverflow.com/questions/43448993/asp-net-core-mvc-app-settings) <- 実例
* [Configure an ASP.NET Core App](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?tabs=basicconfiguration) <- 公式 Doc
* [Working with multiple environments](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments) <- 複数の環境設定の公式 Doc
* [DependencyInjection](https://github.com/aspnet/DependencyInjection) <- 内部で使われているDIライブラリ