【C#】WebApplicationBuilderで自作の定義ファイルを扱う

WebApplicationBuilder などでは定義ファイルとして appsettings.jsonappsettings.Development.json は最初から利用できます。

この定義ファイルの仕組みに自作の JSON ファイルを追加しIConfiguration 経由で内容を利用できるようにしたいと思います。

確認環境

  • .NET 6以降
  • VisualStudio 2022

WebApplicationBuilder が .NET6 以降から追加された機能です。

定義の追加方法と読み取り

WebApplicationBuilder に定義を追加する方法は以下の通りです。

// Hostの作成
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
// 以下アプリの設定の追加
// builder.Services.Xxxx
// ....

// 自作のJSON定義ファイルを追加
//   第2引数(bool) = true: あっても無くてもよい / false: 必須で無いとエラー
//   第3引数(bool) = true: 内用に更新があったら即座にリロードする / false: リロードしない
builder.Configuration.AddJsonFile("myAppsettings.json", false, false);

今回はアプリケーションルートフォルダに以下の JSON を配置したいと思います。

// myAppsettings.json
{
    // この定義ファイル内は特別にコメントが使える
    "MySettings": {
        // 文字列の定義
        "StringKey": "This is string.",
        // 数値の定義
        "IntKey": 10,
        "DoubleKey": 45.67,
        // 真偽値の定義
        "BoolKey": true
    }
}

この JSON 内では特別に // を使ったコメントが使用できます。

手動で読み取る

まずは手動で読み取りたいと思います。

セクションとキーを文字列で指定して取得した値を目的の型に変換します。

IConfigurationSection のインデクサー [] の値の取得は string 型なので int 型など文字列以外の場合目的の型に自分で変換する必要があります。

// WebApplicationBuilderをビルドする
WebApplication app = builder.Build();
// 定義情報を取得する
IConfiguration config = app.Configuration;
// 定義内のセクションを取得する
IConfigurationSection section = app.Configuration.GetSection("MySettings");

string stringValue = section["StringKey"]; // 存在しない場合null

if (int.TryParse(section["IntKey"], out var intValue))
{
    // 値が取れた時の処理
}
if (double.TryParse(section["DoubleKey"], out var doubleValue))
{
    // ...
}
if (bool.TryParse(section["DoubleKey"], out var boolValue))
{
    // ...
}

Key が存在しない場合、値が取れないので事前に存在することを保証するか、Key が有っても無くても問題ない事を確認するよう実装します。

毎回こういう処理を書くのが面倒なので操作を簡単にするためのライブラリを以前作成したので併せて紹介します。

takap-tech.com

自動でマッピングする(推奨)

次は自動で読み取る方法です。多少ルールを覚える必要がありますがこちらの方が実装の手間が少ないのでこちらの方法が推奨です。

まずは以下のように設定内容をマッピングするクラスを作成します。

このクラスの定義は以下の制限があります

  • set もしくは init と get の両方が指定された public なプロパティが読み取り対象
    • 但しオプションで非公開なプロパティも指定できる
    • フィールドは読み書きできない
  • ConfigurationKeyName 属性を使うとプロパティ名と異なるキーをバインドできる
public class MySettings
{
    public const string SectionName = "MySettings";

    [ConfigurationKeyName("StringKey")]
    public string StringKey { get; set; }

    [ConfigurationKeyName("IntKey")]
    public int IntValue { get; set; }

    [ConfigurationKeyName("DoubleKey")]
    public double DoubleValue { get; set; }

    [ConfigurationKeyName("BoolKey")]
    public bool BoolValue { get; set; }
}

次に以下のコードを実装します。

// 定義情報を取得する
IConfiguration config = app.Configuration;
// 定義内のセクションを取得する
IConfigurationSection section = app.Configuration.GetSection("MySettings");

// 値をクラスにロードする
MySettings mySetting = app.Configuration.GetSection(MySettings.SectionName).Get<MySettings>();
Trace.WriteLine($"StringValue={mySetting.StringKey}");
Trace.WriteLine($"IntValue={mySetting.IntValue}");
Trace.WriteLine($"DoubleValue={mySetting.DoubleValue}");
Trace.WriteLine($"BoolValue={mySetting.BoolValue}");

これで内容が読み取れるようになります。

DIコンテナに登録する

WepApplication だとサービスクラスは DI 経由でオブジェクトを受け取ることが多いですがこの自作の設定は以下で DIコンテナに登録することがでます。

// Hostの作成
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
// 自作のJSON定義ファイルを追加
builder.Configuration.AddJsonFile("myAppsettings.json", false, false);
// DIコンテナに登録する
builder.Services.Configure<MySettings>(builder.Configuration.GetSection(MySettings.SectionName));

// WebAPI用の設定
builder.Services.AddControllers();
builder.Build();
app.MapControllers();
app.Run();

以下のWeb API 用のコントローラーを追加して Webアプリを実行し、ブラウザから localhost:NNNN/sample/hoge と入力すると DI されてオブジェクトに MySetting が設定されます。

// SampleController.cs
[ApiController]
[Route("sample")]
public class SampleController : ControllerBase
{
    MySettings _mySetting;

    public SampleController(IOptions<MySettings> options)
    {
        _mySetting = options.Value;
    }

    [HttpGet("hoge")]
    public IActionResult DownloadArchive()
    {
        Trace.WriteLine($"StringValue={_mySetting.StringKey}");
        Trace.WriteLine($"IntValue={_mySetting.IntValue}");
        Trace.WriteLine($"DoubleValue={_mySetting.DoubleValue}");
        Trace.WriteLine($"BoolValue={_mySetting.BoolValue}");
        return new ObjectResult("hogehoge");
    }
}