.NET でカスタム構成プロバイダーを実装する
JSON、XML、INI ファイルなどの一般的な構成ソースに使用できる構成プロバイダーは多数あります。 使用できるプロバイダーの 1 つがアプリケーションのニーズに合わない場合は、必要に応じてカスタム構成プロバイダーを実装します。 この記事では、構成ソースとしてデータベースに依存するカスタム構成プロバイダーを実装する方法について説明します。
カスタム構成プロバイダー
このサンプル アプリは、Entity Framework (EF) Core を使用してデータベースから構成のキーと値のペアを読み取る、基本的な構成プロバイダーを作成する方法を示します。
プロバイダーの特徴は次のとおりです。
- EF のメモリ内データベースは、デモンストレーションのために使用されます。
- 接続文字列を必要とするデータベースを使用するには、中間構成から接続文字列を取得します。
- プロバイダーは、起動時に、構成にデータベース テーブルを読み取ります。 プロバイダーは、キー単位でデータベースを照会しません。
- 変更時に再度読み込む機能は実装されていません。このため、アプリの起動後にデータベースを更新しても、アプリの構成には影響がありません。
データベースに構成値を格納するための Settings
レコード型のエンティティを定義します。 たとえば、Models フォルダーに Settings.cs ファイルを追加できます。
namespace CustomProvider.Example.Models;
public record Settings(string Id, string? Value);
レコード型の詳細については、「C# のレコード型」を参照してください。
構成した値を格納し、その値にアクセスするための EntityConfigurationContext
を追加します。
Providers/EntityConfigurationContext.cs:
using CustomProvider.Example.Models;
using Microsoft.EntityFrameworkCore;
namespace CustomProvider.Example.Providers;
public sealed class EntityConfigurationContext(string? connectionString) : DbContext
{
public DbSet<Settings> Settings => Set<Settings>();
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
_ = connectionString switch
{
{ Length: > 0 } => optionsBuilder.UseSqlServer(connectionString),
_ => optionsBuilder.UseInMemoryDatabase("InMemoryDatabase")
};
}
}
OnConfiguring(DbContextOptionsBuilder) をオーバーライドすると、適切なデータベース接続を使用できます。 たとえば、接続文字列が指定されている場合は SQL Server に接続でき、そうでない場合は、メモリ内のデータベースに依存することができます。
IConfigurationSource を実装するクラスを作成します。
Providers/EntityConfigurationSource.cs:
using Microsoft.Extensions.Configuration;
namespace CustomProvider.Example.Providers;
public sealed class EntityConfigurationSource(
string? connectionString) : IConfigurationSource
{
public IConfigurationProvider Build(IConfigurationBuilder builder) =>
new EntityConfigurationProvider(connectionString);
}
ConfigurationProvider から継承して、カスタム構成プロバイダーを作成します。 データベースが空だった場合、構成プロバイダーはこれを初期化します。 構成キーでは大文字と小文字が区別されないため、データベースの初期化に使用されるディクショナリは、大文字と小文字を区別しない比較子 (StringComparer.OrdinalIgnoreCase) を使用して作成されます。
Providers/EntityConfigurationProvider.cs:
using CustomProvider.Example.Models;
using Microsoft.Extensions.Configuration;
namespace CustomProvider.Example.Providers;
public sealed class EntityConfigurationProvider(
string? connectionString)
: ConfigurationProvider
{
public override void Load()
{
using var dbContext = new EntityConfigurationContext(connectionString);
dbContext.Database.EnsureCreated();
Data = dbContext.Settings.Any()
? dbContext.Settings.ToDictionary(
static c => c.Id,
static c => c.Value, StringComparer.OrdinalIgnoreCase)
: CreateAndSaveDefaultValues(dbContext);
}
static Dictionary<string, string?> CreateAndSaveDefaultValues(
EntityConfigurationContext context)
{
var settings = new Dictionary<string, string?>(
StringComparer.OrdinalIgnoreCase)
{
["WidgetOptions:EndpointId"] = "b3da3c4c-9c4e-4411-bc4d-609e2dcc5c67",
["WidgetOptions:DisplayLabel"] = "Widgets Incorporated, LLC.",
["WidgetOptions:WidgetRoute"] = "api/widgets"
};
context.Settings.AddRange(
[.. settings.Select(static kvp => new Settings(kvp.Key, kvp.Value))]);
context.SaveChanges();
return settings;
}
}
AddEntityConfiguration
拡張メソッドを使用すると、基礎となる ConfigurationManager
インスタンスに構成ソースを追加できます。
Extensions/ConfigurationManagerExtensions.cs:
using CustomProvider.Example.Providers;
namespace Microsoft.Extensions.Configuration;
public static class ConfigurationManagerExtensions
{
public static ConfigurationManager AddEntityConfiguration(
this ConfigurationManager manager)
{
var connectionString = manager.GetConnectionString("WidgetConnectionString");
IConfigurationBuilder configBuilder = manager;
configBuilder.Add(new EntityConfigurationSource(connectionString));
return manager;
}
}
ConfigurationManager は IConfigurationBuilder と IConfigurationRoot の両方の実装であるため、拡張メソッドは接続文字列の構成にアクセスし、EntityConfigurationSource
を追加できます。
次のコードでは、Program.cs でカスタムの EntityConfigurationProvider
を使用する方法を示します。
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using CustomProvider.Example;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Configuration.AddEntityConfiguration();
builder.Services.Configure<WidgetOptions>(
builder.Configuration.GetSection("WidgetOptions"));
using IHost host = builder.Build();
WidgetOptions options = host.Services.GetRequiredService<IOptions<WidgetOptions>>().Value;
Console.WriteLine($"DisplayLabel={options.DisplayLabel}");
Console.WriteLine($"EndpointId={options.EndpointId}");
Console.WriteLine($"WidgetRoute={options.WidgetRoute}");
await host.RunAsync();
// Sample output:
// WidgetRoute=api/widgets
// EndpointId=b3da3c4c-9c4e-4411-bc4d-609e2dcc5c67
// DisplayLabel=Widgets Incorporated, LLC.
プロバイダーの使用
カスタム構成プロバイダーを使用するには、オプション パターンを使用できます。 サンプル アプリを所定の場所に配置し、ウィジェットの設定を表すオプション オブジェクトを定義します。
namespace CustomProvider.Example;
public class WidgetOptions
{
public required Guid EndpointId { get; set; }
public required string DisplayLabel { get; set; } = null!;
public required string WidgetRoute { get; set; } = null!;
}
Configure を呼び出すと、TOptions
がバインドする構成インスタンスが登録されます。
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using CustomProvider.Example;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Configuration.AddEntityConfiguration();
builder.Services.Configure<WidgetOptions>(
builder.Configuration.GetSection("WidgetOptions"));
using IHost host = builder.Build();
WidgetOptions options = host.Services.GetRequiredService<IOptions<WidgetOptions>>().Value;
Console.WriteLine($"DisplayLabel={options.DisplayLabel}");
Console.WriteLine($"EndpointId={options.EndpointId}");
Console.WriteLine($"WidgetRoute={options.WidgetRoute}");
await host.RunAsync();
// Sample output:
// WidgetRoute=api/widgets
// EndpointId=b3da3c4c-9c4e-4411-bc4d-609e2dcc5c67
// DisplayLabel=Widgets Incorporated, LLC.
上記のコードは、構成の "WidgetOptions"
セクションから WidgetOptions
オブジェクトを構成します。 これにより、オプション パターンが有効になり、EF 設定の依存関係の挿入対応の IOptions<WidgetOptions>
表現が公開されます。 これらのオプションは、最終的にカスタム構成プロバイダーから提供されます。
関連項目
.NET