気づけば Azure Functions V2 のC#で DI ( Dependency Injection )が正式にサポートされましたね。依存性の注入とか依存関係の注入とか、MSのドキュメントだと依存関係の挿入とかってやつです。
実装のざっくりな要点は以下だと思ってます。
Startup.cs
で DI の設定する- DI の機能自体は ASP.NET Core ベース
- Functions のクラスのコンストラクターでインジェクト( つまーり class/methodは、従来の static ではなくなった)
試してみましょう。
VS2019でプロジェクトの作成
プロジェクトの作成
とりあえず新しいプロジェクトを作ってみましょう。今回は、VS2019 で新しいプロジェクトを追加します。テンプレートはもちろん C# の Azure Functions を選択し、次へ をクリックします。
プロジェクト名と場所を適当に入力して 作成 をクリックします。
ここでは、V2 の選択は必須です。
後は適当ですが、Http trigger で作ります。Access rights は、検証しやすいように Anonymous にしてます。
Nuget Package
DI をするにあたり、以下2点が必須です。
- Nuget Package Microsoft.NET.Sdk.Functionsのバージョンが v1.0.27 以上
- Nuget Package Microsoft.Azure.Functions.Extensions のインストール
VS2019 っぽく操作感でで ctrl
+ Q
> 「nuget」と入力し、「ソリューションのNugetパッケージの管理...」を選択します。
このブログを書いてる時点では Microsoft.NET.Sdk.Functions のバージョンが低いので最新(現時点では v1.0.27)に挙げておきます。
あとは、Microsoft.Azure.Functions.Extensions を検索してインストールしておきましょう。
既にプレビューが外れ v1.0.0 になっていますね♪。
ついでに インジェクトするように以下のサンプルの 2 つの class を用意しました。それぞれクラス生成時に Guid を付与しました。あとでライフサイクルの違いを可視化するためです。
DI のセットアップ
やることは以下2点
FunctionsStartup
を継承したクラスを用意して、DI- Functions のコンストラクターで Inject する
Startup class の作成
ASP.NET Core をやってる方ならおなじみの host 起動時に Startup class の Configure method で DI をするのと類似の仕組みを構築するだけの話です。
クラス名はなんでもいいんですが、今回は普通に Startup にします。ということで、プロジェクトに「Startup.cs」を追加します。実装の要点としては、
- assembly attribute の設定を追加する(5行目)。type に startup のクラスを指定する。
- class に
FunctionsStartup
を継承させる(9行目)。 Configure
メソッドで DI を設定する(11-15行目)。
ここでは、IMyService
インターフェースに対してMyService
をScoped で(13行目)、 MyClient
は 直接 Singleton で(14行目)登録しました。
環境変数は、14行目のように Environment.GetEnvironmentVariable(...
で取得できます。別の方法として IConfiguration
がデフォルトで登録されているので、それを呼び出して利用するとか直接 MyClient
class のコンストラクターに引数を設定して渡すとかもできますね。もうちょっと複雑だったら、IConfiguration から環境変数をまとめる適当なクラス作って Configure メソッドでインスタンス化して利用した方が楽なケースもありそうです。
環境変数の設定方法については後ほど書きます。
DI の話に戻りますが、設定方法は ASP.NET Core 同様です。気にするところは Singleton とか Scoped とかのライフサイクルくらいでしょうか。
むかーしちょっとブログ書いたなー...基本的にはこの頃から変わってないです。
環境変数の設定
簡易にやるなら、ローカルデバッグ時等の設定では、local.settings.json
に記載すればよいです。上記のコード14行目 Environment.GetEnvironmentVariable("MyClientConnectionString")
とあるので、以下のように書けばよいですね。ちなみに値は適当です。
(MyClientConnectionString
以外はデフォルトで入ってくるものだったりしますので説明は省略)。
{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "FUNCTIONS_WORKER_RUNTIME": "dotnet", "MyClientConnectionString": "local-settings-json-connection-string" } }
Azure にデプロイした時は、いつもどおり アプリケーション設定 に書けばよいです。もっと複雑なことをやりたければ、ASP.NET Core でやってるようなことを応用して適当にやればよい感じですね(説明雑)。
Functions の実装
テンプレートで生成された Functions1.cs をいじっていきます。 やることはこんな感じです。
- class と method から
static
をけす。 - コンストラクターを作り、インジェクトするクラスを定義したりする(12~13行目、15~19行目)
あとはメソッドの中で適当に使うだけです。サンプルではいくつかのログをだしてみました。
デバッグで確認すると、Singleton にした MyClient
クラスの MyId プロパティは、Functions が連続でコールされても変わりません。MySerivce
クラスは Scoped なので、Functions がコールされる度に別の MyId になります。
Tips
HttpClient を利用する際
Functions の中で new して使うとかあれなので、DI しておきましょうか。
Nuget package の Microsoft.Extensions.Http をインストールして、Startup の Configure メソッドに以下を書くだけです。
builder.Services.AddHttpClient();
これで IHttpClientFactory
がインジェクトできるようになったので、さっきのコードを編集する場合なら Functions 側のコンストラクターでこんな感じで呼び出すだけです。すげー雑なサンプルですね。
private readonly HttpClient _httpClient; public Function1(IMyService myService, MyClient myClient, IHttpClientFactory httpClientFactory) { _myService = myService; _myClient = myClient; _httpClient = httpClientFactory.CreateClient(); }
HttpClientFactory使い方については昔ブログを書きましたので貼っておきます。
Startup 側で TypedClients や Named Clientsを設定して Functions で HttpClient を渡すとかは用途次第ですね。
注意点
公式ドキュメントの後半にかいてあったことですが、環境変数とか持ってる IConfiguration
はデフォルトで登録されてるので必要に応じて使いましょうって点と、Application Insights 用の ILoggProvider はデフォで登録されてるので、自分で登録する必要ないって点が気になった程度。