在 .NET 中實作自訂設定提供者
有許多組態提供者可用於常見的組態來源,例如 JSON、XML 和 INI 檔案。 當其中一種提供者不符合您應用程式的需求時,您可能需要實作一個自訂組態提供者。 在本文中,您將了解如何實作依賴資料庫作為其組態來源的自訂群組態提供者。
自訂設定提供者
範例應用程式示範如何建立會使用 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>
標記法。 這些選項最終會從自訂群組態提供者提供。