Bagikan melalui


Menerapkan penyedia konfigurasi kustom di .NET

Ada banyak penyedia konfigurasi yang tersedia untuk sumber konfigurasi umum seperti file JSON, XML, dan INI. Anda mungkin perlu menerapkan penyedia konfigurasi kustom ketika salah satu penyedia yang tersedia tidak sesuai dengan kebutuhan aplikasi Anda. Dalam artikel ini, Anda akan mempelajari cara menerapkan penyedia konfigurasi kustom yang bergantung pada database sebagai sumber konfigurasinya.

Penyedia konfigurasi kustom

Aplikasi sampel menunjukkan cara membuat penyedia konfigurasi dasar yang membaca pasangan kunci-nilai konfigurasi dari database menggunakan Entity Framework (EF) Core.

Penyedia memiliki karakteristik sebagai berikut:

  • Database dalam memori EF digunakan untuk tujuan demonstrasi.
    • Untuk menggunakan database yang memerlukan string koneksi, dapatkan string koneksi dari konfigurasi sementara.
  • Penyedia membaca tabel database ke dalam konfigurasi saat startup. Penyedia tidak meminta database berdasarkan basis per kunci.
  • Reload-on-change tidak diimplementasikan, jadi memperbarui database setelah aplikasi dimulai tidak akan memengaruhi konfigurasi aplikasi.

Settings Tentukan entitas jenis rekaman untuk menyimpan nilai konfigurasi dalam database. Misalnya, Anda dapat menambahkan file Pengaturan.cs di folder Model Anda:

namespace CustomProvider.Example.Models;

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

Untuk informasi tentang jenis catatan, lihat Jenis rekaman di C#.

Tambahkan EntityConfigurationContext untuk menyimpan dan mengakses nilai yang dikonfigurasikan.

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

Dengan mengesampingkan OnConfiguring(DbContextOptionsBuilder) Anda bisa menggunakan koneksi database yang sesuai. Misalnya, jika string koneksi disediakan, Anda dapat tersambung ke SQL Server, jika tidak, Anda dapat mengandalkan database dalam memori.

Buat kelas yang mengimplementasikan IConfigurationSource.

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

Buat penyedia konfigurasi kustom dengan mewarisi dari ConfigurationProvider. Penyedia konfigurasi menginisialisasi database saat kosong. Karena kunci konfigurasi tidak peka terhadap huruf besar/kecil, kamus yang digunakan untuk menginisialisasi database dibuat dengan pembanding yang tidak peka huruf besar/kecil (StringComparer.OrdinalIgnoreCase).

Penyedia/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 Metode ekstensi memungkinkan penambahan sumber konfigurasi ke instans yang mendasarConfigurationManager.

Ekstensi/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 Karena adalah implementasi dan IConfigurationBuilderIConfigurationRoot, metode ekstensi dapat mengakses konfigurasi string koneksi s dan menambahkan EntityConfigurationSource.

Kode berikut menunjukkan cara menggunakan kustom EntityConfigurationProvider di 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.

Mengonsumsi penyedia

Untuk menggunakan penyedia konfigurasi kustom, Anda dapat menggunakan pola opsi. Dengan aplikasi sampel di tempat, tentukan objek opsi untuk mewakili pengaturan widget.

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

Panggilan untuk Configure mendaftarkan instans konfigurasi, yang TOptions mengikat.

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.

Kode sebelumnya mengonfigurasi WidgetOptions objek dari bagian "WidgetOptions" konfigurasi. Ini memungkinkan pola opsi, mengekspos representasi siap injeksi IOptions<WidgetOptions> dependensi dari pengaturan EF. Opsi pada akhirnya disediakan dari penyedia konfigurasi kustom.

Lihat juga