前回は、.NET アセンブリの構造とライブラリの種類を見ました。
今回は、.NET アプリケーションの 設定ファイル を扱います。
接続文字列、ログレベル、外部 API の URL、機能フラグなど、実行環境ごとに変えたい値はソースコードに直接書かず、設定として分離するのが基本です。
設定をコードから分離する理由
設定値をコードに埋め込むと、次のような問題が起きます。
- 開発環境、検証環境、本番環境で値を変えにくい
- 秘密情報がソースコードに混ざりやすい
- 値を変えるだけなのに再ビルドが必要になる
- テスト時に差し替えづらい
.NET では、Microsoft.Extensions.Configuration を使うことで、JSON ファイル、環境変数、コマンドライン引数などを統一的に扱えます。
appsettings.json を読み込む
まず、次のような appsettings.json を用意します。
{
"App": {
"Name": "Sample Tool",
"RetryCount": 3
},
"ConnectionStrings": {
"Default": "Server=localhost;Database=Sample;Trusted_Connection=True;"
}
}
コンソールアプリで読み込む例です。
using Microsoft.Extensions.Configuration;
IConfiguration configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.Build();
string? appName = configuration["App:Name"];
int retryCount = configuration.GetValue<int>("App:RetryCount");
string? connectionString = configuration.GetConnectionString("Default");
Console.WriteLine(appName);
Console.WriteLine(retryCount);
Console.WriteLine(connectionString);
階層構造は : で指定します。
App.Name ではなく、App:Name と書く点に注意します。
複数の設定ファイルを使う
環境ごとに設定を分けたい場合は、複数ファイルを重ねて読み込めます。
// appsettings.json
{
"App": {
"Name": "Sample Tool",
"RetryCount": 3
}
}
// appsettings.Development.json
{
"App": {
"RetryCount": 1
}
}
読み込み側です。
using Microsoft.Extensions.Configuration;
string environment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")
?? "Production";
IConfiguration configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
.Build();
Console.WriteLine(configuration["App:Name"]);
Console.WriteLine(configuration["App:RetryCount"]);
後から読み込んだ設定が、前に読み込んだ設定を上書きします。
この例では、開発環境なら RetryCount が 1 になります。
オブジェクトへ設定を割り当てる
設定値を毎回文字列キーで取り出すと、タイプミスに弱くなります。
まとまった設定はクラスへバインドすると扱いやすくなります。
public class AppOptions
{
public string Name { get; set; } = "";
public int RetryCount { get; set; }
}
using Microsoft.Extensions.Configuration;
IConfiguration configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
var options = new AppOptions();
configuration.GetSection("App").Bind(options);
Console.WriteLine(options.Name);
Console.WriteLine(options.RetryCount);
Get<T>() を使うと、より短く書けます。
AppOptions? options = configuration
.GetSection("App")
.Get<AppOptions>();
ASP.NET Core や Generic Host を使う場合は、Options パターンとして DI に登録するのが一般的です。
builder.Services.Configure<AppOptions>(
builder.Configuration.GetSection("App"));
環境変数を設定として使う
環境変数は、コンテナーやクラウド環境でよく使われます。
using Microsoft.Extensions.Configuration;
IConfiguration configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true)
.AddEnvironmentVariables()
.Build();
Console.WriteLine(configuration["App:Name"]);
階層構造を環境変数で表す場合、環境によっては : が使いづらいため、__ を使います。
App__Name="Production Tool"
App__RetryCount=5
.NET の構成システムは、__ を階層区切りとして扱います。
コマンドライン引数を設定として使う
コマンドライン引数も設定ソースにできます。
using Microsoft.Extensions.Configuration;
IConfiguration configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true)
.AddCommandLine(args)
.Build();
Console.WriteLine(configuration["App:Name"]);
実行時に次のように指定できます。
dotnet run --App:Name "Command Line Tool"
設定ファイル、環境変数、コマンドライン引数を組み合わせる場合、後から追加した設定ソースほど優先されます。
IConfiguration configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true)
.AddEnvironmentVariables()
.AddCommandLine(args)
.Build();
この例では、コマンドライン引数が最も優先されます。
追加の設定値を扱う
設定には、文字列だけでなく数値、真偽値、配列も使えます。
{
"FeatureFlags": {
"UseNewImporter": true
},
"AllowedHosts": [
"localhost",
"example.com"
]
}
bool useNewImporter = configuration.GetValue<bool>(
"FeatureFlags:UseNewImporter");
string[] hosts = configuration
.GetSection("AllowedHosts")
.Get<string[]>() ?? Array.Empty<string>();
Console.WriteLine(useNewImporter);
Console.WriteLine(string.Join(", ", hosts));
設定値は外部から変えられるため、アプリケーション側では妥当性確認も重要です。
秘密情報を設定ファイルへ直接書かない
接続文字列や API キーを appsettings.json に直接書くと、リポジトリに混ざる危険があります。
開発環境ではユーザーシークレット、本番環境では環境変数、Key Vault、シークレット管理サービスなどを使うのが安全です。
dotnet user-secrets init
dotnet user-secrets set "Api:Key" "development-secret"
ユーザーシークレットは開発用です。
本番の秘密情報管理には、環境に合った仕組みを使います。
まとめ
設定ファイルは、アプリケーションの振る舞いを環境ごとに変えるための重要な仕組みです。
.NET の構成システムでは、JSON ファイル、環境変数、コマンドライン引数などを統一的に扱えます。
複数の設定ソースを重ねるときは、後から追加したものが前の値を上書きする点を覚えておきましょう。
次回は、クラスライブラリを作成し、別のアプリケーションから参照して利用する流れを見ていきます。