在 .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;
    }
}

由於 ConfigurationManagerIConfigurationBuilderIConfigurationRoot 的實作,因此擴充方法可以存取連接字串設定並新增 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> 標記法。 這些選項最終會從自訂群組態提供者提供。

另請參閱