ASP.NET Core Webとバッチで必要な設定だけを登録する
Web APIとバッチ処理で同じライブラリを使っていると、設定の読み込みもまとめたくなります。
ただし、すべての設定を一括で登録・検証すると、バッチでは使わないWeb専用の設定まで、バッチの起動に必要になることがあります。
今回は、設定を検証する方法は共有し、どの設定を使うかはアプリごとに選ぶ構成を紹介します。
この記事では、公開サイトのURLを使うWeb APIと、処理件数の設定を使うバッチを例にします。それぞれが利用する設定だけを登録する構成にします。
共通の登録処理が不要な依存を作ることがある
たとえば、次の2種類の設定を考えます。
| 設定 | Web API | バッチ |
|---|---|---|
| 公開サイトのURL | 使う | この例では使わない |
| 1回に整理するデータ件数 | 使わない | 使う |
両方のアプリが同じ「全設定を登録するメソッド」を呼び、すべてを起動時に検証すると、バッチにも公開サイトのURLが必要になります。
使わない設定をダミーの値で埋めると、どの値が本当に必要なのかがわかりにくくなります。必要な設定の不足は検出しつつ、使わない設定を要求しない構成にします。
設定の型に検証条件を書く
以下は.NET 8のASP.NET Coreプロジェクトで使う例です。
AppOptions.csusing System.ComponentModel.DataAnnotations;
public sealed class PublicSiteOptions
{
[Required]
[Url]
public string BaseUrl { get; set; } = "";
}
public sealed class CleanupOptions
{
[Range(1, 1000)]
public int BatchSize { get; set; }
}Requiredは必須、UrlはURL形式、Rangeは数値の範囲を表します。今回のBatchSizeには既定値を入れていないため、設定がなければ0となり検証に失敗します。
これらの属性を書くだけで、自動的に起動時の検証が行われるわけではありません。次の登録処理と組み合わせます。
設定を型として読み込む基本は、以下の記事を参照してください。
IOptionsでappsettings.jsonの設定値を読み込む方法
検証を含む登録方法を1つにする
OptionsRegistration.csusing Microsoft.Extensions.DependencyInjection;
public static class OptionsRegistration
{
public static IServiceCollection AddCheckedOptions<T>(
this IServiceCollection services,
string sectionName)
where T : class
{
services.AddOptions<T>()
.BindConfiguration(sectionName)
.ValidateDataAnnotations()
.ValidateOnStart();
return services;
}
}このメソッドは、指定した設定を読み込み、属性による検証を登録し、アプリ起動時に検証するところまでまとめています。
この3つを共通の登録メソッドへまとめると、設定を追加するときに、ある型だけ起動時検証を付け忘れることを減らせます。
ValidateOnStartはホストの開始時に検証を行います。サービスの登録やBuildだけで検証完了と考えず、起動まで確認します。Optionsの検証については、Microsoft Learnの説明も参照してください。
通常のコンソールアプリへ移す場合は、ホストの構成やOptions関連パッケージの参照も必要です。ここではASP.NET Coreの共有フレームワークを利用する例に絞っています。
登録する対象はアプリごとに決める
Web APIでは、公開サイトの設定だけを登録します。
Web側のProgram.csに追加する処理builder.Services.AddCheckedOptions<PublicSiteOptions>("PublicSite");バッチ側では、整理処理の設定だけを登録します。
バッチ側の登録処理builder.Services.AddCheckedOptions<CleanupOptions>("Cleanup");それぞれの設定ファイルの例です。
Web側のappsettings.json{
"PublicSite": {
"BaseUrl": "https://example.com"
}
}バッチ側のappsettings.json{
"Cleanup": {
"BatchSize": 100
}
}設定が増えたら、Web用とバッチ用に登録メソッドを用意し、それぞれ必要な型を列挙すると見通しがよくなります。設定ファイルもアプリごとに持つことで、起動に必要な値を確認しやすくなります。
ここでいうアプリの区別は、開発環境と本番環境の区別とは別です。Webの開発環境、Webの本番環境、バッチの開発環境、バッチの本番環境で、必要な設定をそろえます。
型を共有しても値は同じとは限らない
同じストレージ用の設定クラスを使っていても、Webとバッチで接続する先が異なる場合があります。
共有したいのは、項目名と検証条件です。環境ごとの値まで、必ず同じファイルへ置く必要はありません。
反対に、同じキューを使う必要があるなど、アプリ間で値を一致させる条件は、個々の設定検証だけでは確認できない場合があります。デプロイ設定や結合テストで確認します。
URL形式が正しくても、そのURLに接続できるとは限りません。認証情報の権限や、接続先サービスの稼働状況も別の確認です。
起動時の設定検証で何を確認し、接続確認で何を確認するかを分けます。
設定を追加するときの確認項目
設定クラスへ項目を追加するだけでなく、それを使うアプリの登録と設定値も確認します。
| 確認すること | 期待する結果 |
|---|---|
| Webの公開URLを未設定にする | Webの起動時検証に失敗する |
| バッチの件数を0にする | バッチの起動時検証に失敗する |
| バッチに公開URLを設定しない | 使わない設定なので、それを理由には失敗しない |
| Webにバッチ用の設定を置かない | 使わない設定なので、それを理由には失敗しない |
| 新しい必須設定を追加する | 利用するアプリの登録と環境設定も更新する |
登録対象から外せば、どんなコードでもその設定を使えなくなるわけではありません。実際にサービスが利用する設定と登録内容が一致しているかも確認します。
アプリが1つなら無理に分けない
Web APIだけで完結しているなら、登録メソッドを用途ごとに細かく分けすぎる必要はありません。
分ける目安は、使わない設定を用意しないと起動できない、別のアプリのための設定変更に巻き込まれる、といった状況です。検証の書き方を共有することと、すべての設定を一括登録することを区別すると、必要な依存関係を保ちやすくなります。