Back to "Anpassade inställningssektionsextensioner"

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#

Anpassade inställningssektionsextensioner

Friday, 27 September 2024

Inledning

Det verkar som om alla har en version av denna kod, Jag kom först över denna strategi från Filip W Ordförande och nyligen har min gamla kollega Phil Haack hans version.

Bara för att slutföra så här gör jag det.

Varför?

Varför är att göra det lättare att arbeta med konfiguration. Just nu är det bästa sättet att använda IOptions<T> och IOptionsSnapshot<T> Men det här är lite jobbigt att jobba med. Detta tillvägagångssätt gör att du kan arbeta med konfigurationen på ett mer naturligt sätt. Begränsningarna är att du inte kan använda IOptions<T> mönster, så att du inte får möjlighet att ladda om konfigurationen utan att starta om programmet. Men ärligt talat är detta något jag nästan aldrig vill göra.

Koden

I min version använder jag den senaste statiska Interface medlemmar för att ange att alla fall av denna klass måste deklarera en Section egendom. Detta används för att få avsnittet från konfigurationen.

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

Så för varje genomförande du sedan ange vilken avsnitt detta ska vara bunden 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 letar den efter ett avsnitt i konfigurationen som heter Newsletter.

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

Vi kommer då att kunna binda detta i Program.cs Filen så här:


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

Vi kan också få värdet av konfigurationen i Program.cs Filen så här:

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

Eller till och med för WebApplicationBuilder så här:

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

Observera: Eftersom byggherren har tillgång till ConfigurationManager Den behöver inte passera in.

Vilket innebär att du nu har alternativ om hur man binder konfiguration.

En annan fördel är att om du behöver använda dessa värden senare i din Program.cs filen då objektet är tillgänglig för dig.

Utökningsmetoden

För att möjliggöra allt detta har vi en ganska enkel förlängningsmetod som gör arbetet med att binda konfigurationen till klassen.

Koden nedan 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 genom följande utökningsklass.

Du kan se att huvudimpulsen för detta är att använda det statiska gränssnittet medlemmar för att ange sektionsnamnet. Detta används sedan för att få avsnittet 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);
    }
}

Använd

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

public class NewsletterService(NewsletterConfig config {
 
}

Slutsatser

Tja det är det... ganska enkelt men det är en teknik jag använder i alla mina projekt. Det är ett trevligt sätt att arbeta med konfiguration och jag tycker att det är lite mer naturligt än den IOptions<T> Mönster.

logo

©2024 Scott Galloway