athome-developer’s blog

不動産情報サービスのアットホームの開発者が発信するブログ

ASP.NET Coreの設定変更を即時反映させる

こんにちは、情報システム部の高野です。
だいぶ間が空いてしまいました。継続は中々難しいですね!
最近は、実プロジェクトにべったりなのでHoloLensの検証が一向に進みません。
この記事も実プロジェクトをやっていて気付いたことです。

環境

.Net Core 2.0.5(SDK2.1.4)
ASP.NET Core2.0
テンプレートはMVCで作成

とりあえず普通に設定を利用してみる

ASP.NET Core のオプションのパターン | Microsoft Docs
公式見れば分かります・・・が、一応書いておきます。

設定(appsettings.json)をアプリケーションで利用するには
まず設定を入れておくクラスを作ります。

public class ApiConfig
{
    public string BaseUrl {get; set;}
}


それよりも前にappsettings.jsonに設定の追加が必要ですね。

"HogeApi": {
  "BaseUrl": "http://example.com/api/"
}

これを受けるのが上記のApiConfigクラスになります。


次にStartUpクラスのConfigureServicesメソッド内に下記を追加します。

services.Configure<ApiConfig>(Configuration.GetSection("HogeApi"));


最後に設定を利用したいクラスのコンストラクタの引数にIOptionsを追加します。

ApiConfig config;

public HomeController(IOptions<ApiConfig> option)
{
    config = option.Value;
}

これでHomeController内でURLを取得することができるようになりました。

アプリケーション起動中に設定を変更し即時反映させる

IOptionsを利用すると設定は読み込めるのですが、アプリケーションを再起動しないと
設定の変更が反映されません。

再起動せずに設定を変更させる方法も公式に載っています。
ASP.NET Core のオプションのパターン | Microsoft Docs
ここに載っているそのまま
IOptionsをIOptionsSnapshotに変更すればいいだけです。
(以前は、別途設定が必要だったような気が・・・新しいテンプレートだと無くなってました)

アクションフィルターで設定を利用する

アクションフィルターで設定を用いて処理したいこともありますよね(きっとあるはず)

まずアクションフィルターを作ります。
こちらもコンストラクタを作成して引数でIOptionsを取るようにします。
(即時反映したいならIOptionsSnapshot)

sealed public class TestFilterAttribute : ActionFilterAttribute
{
    ApiConfig config;

    public TestFilterAttribute(IOptions<ApiConfig> option)
    {
        config = option.Value;
    }
}


利用するコントローラーに属性として付与します。
ただしこの場合は、ServiceFilterを利用する必要があります。

[ServiceFilter(typeof(TestFilterAttribute))]
public class HomeController : Controller


StartUpクラスでインジェクションの設定をします。

services.AddScoped<TestFilterAttribute>();


これでアクションフィルターでも設定が利用できるようになったはずです。
上記にも書きましたがIOptionsSnapshotを使用して即時反映も可能です。

ミドルウェアでも設定が使いたい

ミドルウェアでも設定を使いたい時はあるでしょう(あるある)
ということでミドルウェアでもやってみる。

まずミドルウェアを作ります。
これも同じようにIOptionsをコンストラクタの引数で取ります。

public class TestMiddleware
{
    readonly RequestDelegate next;

    ApiConfig config;

    public TestMiddleware(RequestDelegate next, IOptions<ApiConfig> options)
    {
        this.next = next;
        config = options.Value;
    }

    public async Task Invoke(HttpContext context)
    {
        await next.Invoke(context);
    }
}


あとは、StartUpクラスでミドルウェアを登録するだけです。

app.UseMiddleware<TestMiddleware>();


これでミドルウェアでも設定が利用できるようになりました。
即時反映したいならIOptionsSnapshotを使います・・・とはいかないのです!
ミドルウェアのIOptionsをIOptionsSnapshotに変更すると下記のエラーが発生します。

An unhandled exception of type 'System.InvalidOperationException' occurred in Microsoft.AspNetCore.Hosting.dll: 'Cannot resolve scoped service 'Microsoft.Extensions.Options.IOptionsSnapshot`1[OptionsStudy.ApiConfig]' from root provider.'

どうやらScopedで作成したServiceはミドルウェアでは利用できないようです。
IOptionsSnapshotはAddScopedメソッドで登録されています。
IOptionsはAddSingletonメソッドでした。

それでは、ミドルウェアではIOptionsSnapshotは利用できず即時反映はできないのか?
ところができるのです。

コンストラクタの引数を消してInvokeメソッドの方に移すだけでした。
(これが分からず苦労した)

public async Task Invoke(HttpContext context, IOptionsSnapshot<ApiConfig> options)
{
    var conf = options.Value;
   // ・・・
    await next.Invoke(context);
}

まとめ

IOptionsSnapshotをインジェクションすればアプリケーションを再起動しないでも
設定の変更を反映することができる。
でもミドルウェアは、コンストラクタの引数ではなくInvokeメソッドの引数なので注意!




弊社ではエンジニアを募集しています。
興味がある方は下記からエントリお願いします。
athome-inc.jp