Back to "Custom Konfigurationssektionens förlängningar"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

ASP.NET C#

Custom Konfigurationssektionens förlängningar

Friday, 27 September 2024

Inleiding

Det verkar som att alla har en version av den här koden. Filip W och nyligen har min gamla kollega Phil Haack hans version.

Bara för att färdigställa så här gör jag det.

Varför?

Varför är det lättare att jobba med konfigurationen. Just nu är det första sättet IOptions<T> och IOptionsSnapshot<T> men det är lite svårt att jobba med den. Begränsningarna är att du inte kan använda IOptions<T> mönster, så du inte får förmågan att ladda om konfigurationen utan att starta applikationen igen . men ärligt talat är det något jag nästan aldrig vill göra

Koden

I min version använder jag de senaste statiska gränssnittmedlemmarna för att specifikera att alla instanser i denna klass måste Section property. Detta används för att få sektionen från konfigurationen

public interface IConfigSection {
    public static abstract string Section { get; }
}

Så för varje implementering så specifikerar du vilken sektion den ska vara sammansluten till

public class NewsletterConfig : IConfigSection
{
    public static string Section => "Newsletter";
    
    public string SchedulerServiceUrl { get; set; } = string.Empty;
    public string AppHostUrl { get; set; } = string.Empty;
}

I det här fallet tittar ' på en sektion i konfigurationen som kallas Newsletter.

  "Newsletter": {
      "SchedulerServiceUrl" : "http://localhost:5000",
        "AppHostUrl" : "https://localhost:7240"
    
  }

Vi kommer då kunna binda detta i Program.cs file like so:


var builder = WebApplication.CreateBuilder(args);
var config = builder.Configuration;
services.ConfigurePOCO<NewsletterConfig>(config);

Vi kan också få värdet på konfigurationen i Program.cs file like so:

var newsletterConfig = services.ConfigurePOCO<NewsletterConfig>(config);

Eller till och med WebApplicationBuilder som så

var newsletterConfig = builder.Configure<NewsletterConfig>();

Nota: Eftersom byggaren har tillgång till ConfigurationManager det behöver inte passa in i

Det betyder att du nu har möjligheter att binda config.

En annan fördel är att om du behöver använda dessa värden senare i ditt Program.cs så är objektet tillgängligt för dig.

Förlängningsmetoden

För att göra allt detta har vi en ganska enkel förlängningsmetod som gör jobbet med att binda konfigurationen till klass.

Koden härunder tillåter följande:

// These get the values and bind them to the class while adding these to Singleton Scope
var newsletterConfig = services.ConfigurePOCO<NewsletterConfig>(config);
var newsletterConfig = services.ConfigurePOCO<NewsletterConfig>(configuration.GetSection(NewsletterConfig.Section));
// Or for Builder...These obviously only work for ASP.NET Core applications, take them out if you are using this in a different context.
var newsletterConfig = builder.Configure<NewsletterConfig>();
var newsletterConfig = builder.Configure<NewsletterConfig>(NewsletterConfig.Section);
// These just return a dictionary, which can be useful to get all the values in a section
var newsletterConfig = builder.GetConfigSection<NewsletterConfig>();

Allt detta är aktiverat av den här rozszerzensklassen.

Man kan se att den huvudsakliga grejen i detta är att använda de statiska gränssnittmedlemmarna för att definiera sektionens namn. Detta används sedan för att få ut sektionen från konfigurationen

namespace Mostlylucid.Shared.Config;

public static class ConfigExtensions {
    public static TConfig ConfigurePOCO<TConfig>(this IServiceCollection services, IConfigurationSection configuration)
        where TConfig : class, new() {
        if (services == null) throw new ArgumentNullException(nameof(services));
        if (configuration == null) throw new ArgumentNullException(nameof(configuration));
        
        var config = new TConfig();
        configuration.Bind(config);
        services.AddSingleton(config);
        return config;
    }

    public static TConfig ConfigurePOCO<TConfig>(this IServiceCollection services, ConfigurationManager configuration)
        where TConfig : class, IConfigSection, new()
    {
        var sectionName = TConfig.Section;
        var section = configuration.GetSection(sectionName);
        return services.ConfigurePOCO<TConfig>(section);
    }
    
    public static TConfig Configure<TConfig>(this WebApplicationBuilder builder)
        where TConfig : class, IConfigSection, new() {
        var services = builder.Services;
        var configuration = builder.Configuration;
        var sectionName = TConfig.Section;
        return services.ConfigurePOCO<TConfig>(configuration.GetSection(sectionName));
    }
    

    public static TConfig GetConfig<TConfig>(this WebApplicationBuilder builder)
        where TConfig : class, IConfigSection, new() {
        var configuration = builder.Configuration;
        var sectionName = TConfig.Section;
        var section = configuration.GetSection(sectionName).Get<TConfig>();
        return section;
        
    }
    
    public static Dictionary<string, object> GetConfigSection(this IConfiguration configuration, string sectionName) {
        var section = configuration.GetSection(sectionName);
        var result = new Dictionary<string, object>();
        foreach (var child in section.GetChildren()) {
            var key = child.Key;
            var value = child.Value;
            result.Add(key, value);
        }
        
        return result;
    }
    
    public static Dictionary<string, object> GetConfigSection<TConfig>(this WebApplicationBuilder builder)
        where TConfig : class, IConfigSection, new() {
        var configuration = builder.Configuration;
        var sectionName = TConfig.Section;
        return configuration.GetConfigSection(sectionName);
    }
}

I användning

Att använda dessa är ganska enkelt. I alla klasser där du behöver den här konfigurationen kan du helt enkelt injicera den så

public class NewsletterService(NewsletterConfig config {
 
}

I slutsats

Tja, det är "'", det är ... , ganska enkelt men det är en teknik som jag använder i alla mina projekt. Det är ett bra sätt att jobba med konfigurationer och jag tror att "'" är lite mer naturligt än IOptions<T> mönster.

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.