BEACHSIDE BLOG

MicrosoftとかC#を好むレンジャーの個人メモ

Azure Functions V2 の Startup.cs で appsettings.json を読み込む(2019年5月バージョン)

前回書いた Azure Functions の DI の方法だと、ASP.NET Core ではよく使う appsettings.json の読み込みがデフォルトで設定されていません。

local.setting.json ローカルデバッグ用の環境変数の設定はできるけど、
で困るのは Array とか object に対応してないところ。
Cosmos DB のコレクションを定義したいときは配列で定義して、さらにその中でIDやパーティションキーとか定義しておきたいので、Array とか使えないとめんどいです。Azure 上でアプリケーション設定に Array 形式で書くのもめんどいし。

ということで、そこらへんの実装のメモです。

f:id:beachside:20190520165505j:plain:w300

appsettings.json の追加

とりあえずプロジェクトに appsettings.json を追加します。追加したら、この appsettings.json を右クリック > プロパティ を選択 > 出力ディレクトリーにコピー の値を用途に応じて「常にコピーする」または「新しい場合はコピーする」にしておきましょう。これをしないとビルドやデバッグ時に出力ディレクトリーに出力されず読み込めないという凡ミスによる混乱を招きます。

f:id:beachside:20190519185348p:plain

今回はサンプルとして、appsettings.Development.json も追加しました。環境に応じて変更できるかの確認の為です。追加後、こちらも出力ディレクトリーにコピーの設定を忘れずにしましょう。


appsettings.json の中身は、以下のようにしてみました。あえてちょっとごちゃっとしてます。

{
  "SampleConfig": {
    "Name": "sample-config",
    "Items": [
      {
        "Key": "key1",
        "Description": "description-1"
      },
      {
        "Key": "key2",
        "Description": "description-2"
      }
    ]
  }
}

後は、この Json を読み込んだ後に格納する class を作っておきましょう。

    public class SampleConfig
    {
        public string Name { get; set; }
        public Item[] Items { get; set; }
    }

    public class Item
    {
        public string Key { get; set; }
        public string Description { get; set; }
    }

Configure メソッド(Starup.cs)で appsettings.json を読み込む

Startup.cs class の Configure メソッドで読み込むのみです。
ここでの Startup.cs class の Configure メソッドは、前回の DI の設定で使ったものを使います。

blog.beachside.dev

実装方法としては、Azure Functions のランタイムでDIされてる IConfiguration に 自分のカスタムしたい Configuration (今回だと、appsettings.json の読み込み)を追加する っていう簡単な実装です。

解説は後述します。

IHostingEnvironment の取得/ IConfiguration の取得(16行目 ~ 19行目)

appsettings.json を使ってると、appsettings.Development.json とかも使うこともあるでしょう。環境名を取得するのに IHostingEnvironment も取得しておきます。
GetRequiredService メソッドは、Service とれなかったら InvalidOperationException を吐いてくれるいいやつです。
同様に Azure Functions のデフォルトで登録されてる IConfiguration のを取得しています。変数名は defaultConfig で取得しておきました。

注意:

IHostingEnvironment のフルネームは Microsoft.Extensions.Hosting.IHostingEnvironment です。ASP.NET Core で今までよく使っていた Microsoft.AspNetCore.Hosting.IHostingEnvironment ではないです。
ここでの違いは、SDKの内部的に IHostingEnvironmentEnvironmentName プロパティの環境変数の Key が異なります。つまり Azure上でのアプリケーション設定や、ローカルデバッグ実行時の挙動に影響します。

namespace EnvironmentName を設定する環境変数のキー
Microsoft.AspNetCore.Hosting ASPNETCORE_ENVIRONMENT
Microsoft.Extensions.Hosting AZURE_FUNCTIONS_ENVIRONMENT

ということで、ローカルでも Azure のリソースの Azure Functions のアプリケーション設定でも、必要に応じて環境名を設定しましょう。

この設定は、私の環境で動作確認をしたうえでの結果ですが、Microsoft.Extensions.Hosting のソースには key が "environment" って書いてるんですよね...ConfigurationBuilder のAddEnvironmentVariables 時の Prefix は除外した値ってこと?とか不明...
追記: こちらのドキュメントAZURE_FUNCTIONS_ENVIRONMENT についての記載がありました。

f:id:beachside:20190522142840p:plain:w600

今だと私の環境では正常に動き続けてるけど、ここ数日間で何が悪いのかわからんけど動いたり動かなかったりという謎事象が起きてるので今度も大丈夫かなーなんか間違ってるのかなーと不安要素ありです。環境変数を設定してないときのデフォルト値は、ローカルデバッグ時は Development, Azure上では Productionのはずですが、なぜか Azure 上でも Development になったり、設定しても値が反映されないことがあった。。。

そして、Microsoft.Extensions.Hosting.IHostingEnvironment も .NET Core 3.0系から Breaking changeします(issueはこちら)。ここら辺は今後しらばらくチェックが必要ですなぁ。


appsettings.json を読み込む(23行目 ~ 28行目)

23行目は、appsettings.json のファイルパスを取得する部分です。Functions の実際にコールされる関数の方(日本語変やな)だと、ExecutionCotenxtFunctionAppDirectory から取得できますよねー... この値は 実装上 ExecutionContextOptions から取得しているためこんな感じで取得しています。
env.IsDevelopement で If ってますが、とりあえずデバッグ用か Auzre 上かを分けただけなので、応じて書き換える必要があるかもしれません。

25~28 行目appsettings.json と 環境名付きの json ( appsettings.Development.json とか)に対応して json を読み込みます。

31行目で、既存の IConfiguration と今作成した IConfiguration を結合しました。

以降は普通にASP.NET Core のやり方で DI の設定してるだけです。


これで、ローカルデバッグでも Azure 上でも動作することは確認できましたが、23行目の @"D:\home\site\wwwroot" べた書きは微妙過ぎて微妙過ぎて震えますね

23行目: appsettings.json のパスの取得について

現時点での Azure Functions 上でのパス D:\home\site\wwwroot をべた書きではなく何らかの形で取得したいのですがよい手段がない....

ブログポストした当初はべた書きで実装してたんですが更新しました。
appsettings.json のパスは、ExecutionContext の FunctionAppDirectory プロパティが持っているので、それを azure-webjobs-sdk 内でセットしてるのがExecutionContextOptions 。これを ServiceProvider からとろうと....私はwebjobs-sdkみて、型を IConfigureOptions<ExecutionContextOptions> で取得し、その型だと中身を見る手段がない....で詰んだんですが、
そもそも IOptions<ExecutionContextOptions> でとればいけるやんって話をしばやん先生からありがたくいただきました♪ これでトラブルのひとつは解決です!あざます!

といことで、サンプルコードの全体はこちら。

GitHub - beachside-project/azure-functionsV2-DI-sample


参考