BEACHSIDE BLOG

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

Azure Functions V2 での DI ( Dependency Injection ) : 2019年5月編

気づけば Azure Functions V2 のC#で DI ( Dependency Injection )が正式にサポートされましたね。依存性の注入とか依存関係の注入とか、MSのドキュメントだと依存関係の挿入とかってやつです。

実装のざっくりな要点は以下だと思ってます。

  • Startup.cs で DI の設定する
  • DI の機能自体は ASP.NET Core ベース
  • Functions のクラスのコンストラクターでインジェクト( つまーり class/methodは、従来の static ではなくなった)

試してみましょう。

VS2019でプロジェクトの作成

プロジェクトの作成

とりあえず新しいプロジェクトを作ってみましょう。今回は、VS2019 で新しいプロジェクトを追加します。テンプレートはもちろん C# の Azure Functions を選択し、次へ をクリックします。

f:id:beachside:20190508175357p:plain:w800


プロジェクト名と場所を適当に入力して 作成 をクリックします。

f:id:beachside:20190508175507p:plain:w800


ここでは、V2 の選択は必須です。
後は適当ですが、Http trigger で作ります。Access rights は、検証しやすいように Anonymous にしてます。

f:id:beachside:20190508175736p:plain:w800

Nuget Package

DI をするにあたり、以下2点が必須です。

  • Nuget Package Microsoft.NET.Sdk.Functionsのバージョンが v1.0.27 以上
  • Nuget Package Microsoft.Azure.Functions.Extensions のインストール

VS2019 っぽく操作感でで ctrl + Q > 「nuget」と入力し、「ソリューションのNugetパッケージの管理...」を選択します。

f:id:beachside:20190509103255p:plain:w800


このブログを書いてる時点では Microsoft.NET.Sdk.Functions のバージョンが低いので最新(現時点では v1.0.27)に挙げておきます。

f:id:beachside:20190509103646p:plain:w800


あとは、Microsoft.Azure.Functions.Extensions を検索してインストールしておきましょう。
既にプレビューが外れ v1.0.0 になっていますね♪。

f:id:beachside:20190509103753p:plain:w800


ついでに インジェクトするように以下のサンプルの 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 とかのライフサイクルくらいでしょうか。
むかーしちょっとブログ書いたなー...基本的にはこの頃から変わってないです。

blog.beachside.dev


環境変数の設定

簡易にやるなら、ローカルデバッグ時等の設定では、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 になります。

f:id:beachside:20190509125219p:plain:w800


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使い方については昔ブログを書きましたので貼っておきます。

blog.beachside.dev


Startup 側で TypedClients や Named Clientsを設定して Functions で HttpClient を渡すとかは用途次第ですね。

注意点

公式ドキュメントの後半にかいてあったことですが、環境変数とか持ってる IConfiguration はデフォルトで登録されてるので必要に応じて使いましょうって点と、Application Insights 用の ILoggProvider はデフォで登録されてるので、自分で登録する必要ないって点が気になった程度。

参考

docs.microsoft.com