色んな事を書く

シンプルさを極めたエンジニアになりたい

Azure Function での Configure について調べる

.NET の Configure について調べたくなったのでまとめます。

動作環境は以下です。

  • .NET 6
  • C# 10
  • Azure Function v4

GetValue

以下のような json file を考えます。

{
    "TestValue1": "hogehoge",
    "TestValue2": "100",
    "TestValue3": "2023/10/17 12:30:31 +00:00"
}

ConfigurationBinder.GetValueを使えば、key を指定して value を取得することが出来ます。また、取得する際には規定値を指定したり型変換も行ってくれます。

var stringValue = configuration.GetValue("TestValue1", "default value"); // hogehoge
var intValue = configuration.GetValue("TestValue2", 200); // 100
var dateTimeValue = configuration.GetValue("TestValue3", DateTimeOffset.UtcNow); // 2023/10/17 12:30:31 +00:00

第一引数に json の key を指定して、第二引数に規定値を指定しています。第二引数を省略した場合、型パラメータを使って明示的に型指定をしないといけません。その場合、key に該当する value がない時は戻り値は型パラメータで指定した型の初期値になります。

Section

Section という仕組みがあります。簡単に言うと、ネストされたオブジェクトを取り扱うためのものです。公式ドキュメントに沿って以下のような Json file を考えます。

{
        "Section1": {
            "TestValue1": "hogehoge",
            "TestValue2": "fugafuga"
        },
        "Section2": {
            "TestValue2": "fugafuga"
        },
        "Section3": {
            "TestValue3": "piyopiyo"
        }
}

実はこれが罠で、Azure Function で Section を扱うには以下のようにしなければいけないようです。

{
    "Section1__SectionValue1": "hogehoge",
    "Section1__SectionValue2": "fugafuga",
    "Section2__SectionValue2": "fugafuga",
    "Section3__SectionValue3": "piyopiyo"
}

この Json

  • Section1
  • Section2
  • Section3

という 3 つの Section を持つことになります。各 Section はそれぞれ key を持っています。これを IConfiguration を使って扱うにはいくつか方法があります。

まずは Configuration.GetSection を使うパターンです。

var section1 = configuration.GetSection("Section1");
var value1 = section1.GetValue<string>("SectionValue1");  // hogehoge
var value2 = section1.GetValue<string>("SectionValue2");  // fugafuga

と取得する事が出来ます。

もう一つが ConfigurationBinder.GetValue の key parameter に「:」を加える方法です。

var value1 = configuration.GetValue<string>("Section1:SectionValue1"); // hogehoge
var value2 = configuration.GetValue<string>("Section1:SectionValue2"); // fugafuga

key 名を「:」で区切ることでネストされたオブジェクトの値を取得する事が出来ます。

ちなみに、 Configuration.GetSectionにも「:」は指定可能で、その場合はさらにネストされたオブジェクトにアクセスできます。

以下のような json

{
    "Section1__SectionValue2__SubSectionValue1": "sub section value"
}

以下を実行できます。

var subsection = configuration.GetSection("Section1:SectionValue2");
var value1 = subsection.GetValue<string>("SubSectionValue1"); // sub section value

Children

ネストされたオブジェクトを扱うには Children という仕組みもあります。これまでの json と同じものを扱うという前提で、以下のように書きます。

var section = configuration.GetSection("Section1");

if (section.Exists())
{
    foreach (var subSection in section.GetChildren())
    {
        var value1 = subSection.Value;
    }
}

こうすると特に key を指定せずとも全ての key の value を取り出してくれます。Exists は IConfigurationSection に対する拡張メソッドで、ある Section に key が一つでもあれば true、なければ false を返します。内部の実装は Any を使ってますね

Option Pattern と Section

長々と書きましたが、最後に Option Pattern とどう組み合わせようかを書きます。Option Pattern は公式ドキュメントに詳しく書いてあるので読んでいただければと思います。私は「構成値を型化して、Singleton で都合よく使い回す」くらいの理解をしています。

なので Startup で ServiceProvider に登録できるのですが、Section を使うとシンプルに書けます。

builder.Services.Configure<MyOption>();

public class MyOption
{
    public const string SectionName = "MyOption";
    
    public string Name { get; init; }
    
    public int Age { get; init; }
}

また、このパターンを使うときにはネストされたオブジェクトの区切り文字を「:」にしなければいけません (上述の例だと「_」にしていました)。

{
    "MyOption:Name": "taro",
    "MyOption:Age": "10"
}

コードを以下のようにすると、

builder.Services.Configure<MyOption>(configuration.GetSection(MyOption.SectionName));
var myOption = builder.Services.BuildServiceProvider().GetRequiredService<IOptions<MyOption>>().Value;
// myOption.Age => 10
// myOption.Name => "taro"

もしくは以下のようにも出来ます。

builder.Services.AddOptions<MyOption>().Configure<IConfiguration>((options, configuration) =>
{
 
configuration.GetSection(MyOption.SectionName).Bind(options);
});
var myOption = builder.Services.BuildServiceProvider().GetRequiredService<IOptions<MyOption>>().Value;
// myOption.Age => 10
// myOption.Name => "taro"

Json の key 名と class の property 名が一致したら value を代入してくれる感じですね。こうしておくと参照側では IOption<MyOption> を使って構成値にアクセスする事が出来ます。

開発者体験良いですね。色々と応用も効きそう。

ちなみに、Azure Function の構成値は文字列でなけれないけません。なので Section を使うときには「__」や「:」といった予約語を使わなければいけないのです。また、「:」は DI をサポートするためのものです。以下引用です。

Values must be strings and not JSON objects or arrays. Setting names can't include a double underline (__) and shouldn't include a colon (:). Double underline characters are reserved by the runtime, and the colon is reserved to support dependency injection.

learn.microsoft.com