Teilen über


Implementieren eines benutzerdefinierten Konfigurationsanbieters in .NET

Es gibt viele Konfigurationsanbieter für allgemeine Konfigurationsquellen wie JSON-, XML- und INI-Dateien. Möglicherweise müssen Sie einen benutzerdefinierten Konfigurationsanbieter implementieren, wenn einer der verfügbaren Anbieter nicht ihren Anwendungsanforderungen entspricht. In diesem Artikel erfahren Sie, wie Sie einen benutzerdefinierten Konfigurationsanbieter implementieren, der auf einer Datenbank als Konfigurationsquelle basiert.

Benutzerdefinierter Konfigurationsanbieter

Die Beispiel-App veranschaulicht das Erstellen eines grundlegenden Konfigurationsanbieters, der Konfigurationsschlüsselwertpaare aus einer Datenbank mithilfe von Entity Framework (EF) Core liest.

Der Anbieter weist die folgenden Merkmale auf:

  • Die EF-In-Memory-Datenbank wird zu Demonstrationszwecken verwendet.
    • Wenn Sie eine Datenbank verwenden möchten, die eine Verbindungszeichenfolge erfordert, rufen Sie eine Verbindungszeichenfolge aus einer Zwischenkonfiguration ab.
  • Der Anbieter liest eine Datenbanktabelle beim Start in die Konfiguration. Der Anbieter fragt die Datenbank nicht pro Schlüssel ab.
  • Neuladen bei Änderungen ist nicht implementiert. Daher wirkt sich das Aktualisieren der Datenbank nach dem Start der App nicht auf die Konfiguration der App aus.

Definieren Sie eine Settings Datensatztypentität zum Speichern von Konfigurationswerten in der Datenbank. Sie können z. B. eine Settings.cs Datei in Ihrem Ordner "Models " hinzufügen:

namespace CustomProvider.Example.Models;

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

Informationen zu Datensatztypen finden Sie unter Datensatztypen in C#.

Fügen Sie EntityConfigurationContext hinzu, um die konfigurierten Werte zu speichern und auf diese zugreifen.

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

Durch das Überschreiben von OnConfiguring(DbContextOptionsBuilder) können Sie die entsprechende Datenbankverbindung verwenden. Wenn beispielsweise eine Verbindungszeichenfolge bereitgestellt wurde, können Sie eine Verbindung mit SQL Server herstellen, andernfalls könnten Sie sich auf eine In-Memory-Datenbank verlassen.

Erstellen Sie eine Klasse, die das IConfigurationSource implementiert.

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

Erstellen Sie den benutzerdefinierten Konfigurationsanbieter durch Vererbung von ConfigurationProvider. Der Konfigurationsanbieter initialisiert die Datenbank, wenn diese leer ist. Da für Konfigurationsschlüssel die Groß-/Kleinschreibung nicht relevant ist, wird das zum Initialisieren der Datenbank verwendete Wörterbuch mit der Vergleichsfunktion ohne Beachtung der Groß-/Kleinschreibung erstellt (StringComparer.OrdinalIgnoreCase).

Anbieter/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;
    }
}

Eine AddEntityConfiguration Erweiterungsmethode ermöglicht das Hinzufügen der Konfigurationsquelle zur zugrunde liegenden ConfigurationManager Instanz.

Erweiterungen/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;
    }
}

Da ConfigurationManager sowohl eine Implementierung von IConfigurationBuilder als auch von IConfigurationRoot ist, kann die Erweiterungsmethode auf die Konfiguration der Verbindungszeichenfolgen zugreifen und EntityConfigurationSource hinzufügen.

Der folgende Code veranschaulicht die Verwendung der benutzerdefinierten EntityConfigurationProvider in 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.

Anbieter nutzen

Um den benutzerdefinierten Konfigurationsanbieter zu nutzen, können Sie das Optionsmuster verwenden. Definieren Sie mit der Beispiel-App ein Optionsobjekt, das die Widgeteinstellungen darstellt.

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

Ein Aufruf von Configure registriert eine Konfigurationsinstanz, an die TOptions gebunden wird.

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.

Der vorangehende Code konfiguriert das WidgetOptions Objekt aus dem "WidgetOptions" Abschnitt der Konfiguration. Dies ermöglicht das Optionsmuster, indem es eine für die Abhängigkeitsinjektion bereite Darstellung der EF-Einstellungen bereitstellt. Die Optionen werden letztendlich vom benutzerdefinierten Konfigurationsanbieter bereitgestellt.

Siehe auch