Sdílet prostřednictvím


Implementace vlastního zprostředkovatele konfigurace v .NET

Pro běžné zdroje konfigurace, jako jsou soubory JSON, XML a INI, je k dispozici mnoho poskytovatelů konfigurace. Možná budete muset implementovat vlastního zprostředkovatele konfigurace, pokud některý z dostupných poskytovatelů nevyhovuje potřebám vaší aplikace. V tomto článku se dozvíte, jak implementovat vlastního zprostředkovatele konfigurace, který jako zdroj konfigurace spoléhá na databázi.

Vlastní zprostředkovatel konfigurace

Ukázková aplikace ukazuje, jak vytvořit základního zprostředkovatele konfigurace, který čte páry klíč-hodnota konfigurace z databáze pomocí Entity Framework (EF) Core.

Zprostředkovatel má následující vlastnosti:

  • Pro účely ukázky se používá databáze EF v paměti.
    • Pokud chcete použít databázi, která vyžaduje připojovací řetězec, získejte připojovací řetězec z dočasné konfigurace.
  • Zprostředkovatel při spuštění přečte databázovou tabulku do konfigurace. Zprostředkovatel neprovádí dotazy do databáze pro jednotlivé klíče.
  • Opětovné načtení změny se neimplementuje, takže aktualizace databáze po spuštění aplikace neovlivní konfiguraci aplikace.

Definujte entitu typu záznamu Settings pro ukládání hodnot konfigurace v databázi. Do složky Models můžete například přidat soubor Nastavení.cs:

namespace CustomProvider.Example.Models;

public record Settings(string Id, string? Value);

Informace o typech záznamů najdete v tématu Typy záznamů v jazyce C#.

Přidejte EntityConfigurationContext pro ukládání a přístup k nakonfigurovaným hodnotám.

Poskytovatelé/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")
        };
    }
}

Přepsáním OnConfiguring(DbContextOptionsBuilder) můžete použít příslušné připojení k databázi. Pokud jste například zadali připojovací řetězec, mohli byste se připojit k SQL Serveru, jinak byste mohli spoléhat na databázi v paměti.

Vytvořte třídu, která implementuje IConfigurationSource.

Poskytovatelé/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);
}

Vytvořte vlastního zprostředkovatele konfigurace děděním hodnot z ConfigurationProvider. Zprostředkovatel konfigurace inicializuje databázi, když je prázdná. Protože konfigurační klíče nerozlišují velká a malá písmena, je slovník použitý k inicializaci databáze vytvořen s porovnávačem nerozlišujícím velká a malá písmena (StringComparer.OrdinalIgnoreCase).

Poskytovatelé/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;
    }
}

Metoda AddEntityConfiguration rozšíření umožňuje přidání zdroje konfigurace do základní ConfigurationManager instance.

Rozšíření/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;
    }
}

Vzhledem k tomu, že je implementace ConfigurationManagerIConfigurationBuilder a IConfigurationRoot, rozšíření metoda může přistupovat k připojovací řetězec konfigurace a přidat EntityConfigurationSource.

Následující kód ukazuje, jak použít vlastní EntityConfigurationProvider v Program.cs:

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.

Využívání poskytovatele

Pokud chcete využívat vlastního zprostředkovatele konfigurace, můžete použít vzor možností. Když je ukázková aplikace na místě, definujte objekt možností, který bude představovat nastavení widgetu.

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!;
}

Volání pro Configure registraci instance konfigurace, která TOptions je svázaná s.

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.

Předchozí kód nakonfiguruje WidgetOptions objekt z "WidgetOptions" oddílu konfigurace. To umožňuje vzor možností, který vystavuje reprezentaci nastavení EF připravenou IOptions<WidgetOptions> pro injektáž závislostí. Možnosti jsou nakonec poskytovány od vlastního zprostředkovatele konfigurace.

Viz také