Back to "Estensioni personalizzate delle sezioni di configurazione"

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#

Estensioni personalizzate delle sezioni di configurazione

Friday, 27 September 2024

Introduzione

Sembra che tutti abbiano una versione di questo codice, mi sono imbattuto in questo approccio da Filip W e recentemente il mio vecchio collega Phil Haack ha la sua versione.

Solo per completare questo è come lo faccio io.

Perché?

Il motivo è quello di rendere più facile lavorare con la configurazione. Al momento la via principale sta usando IOptions<T> e IOptionsSnapshot<T> Ma questo e' un po' un dolore con cui lavorare. Questo approccio consente di lavorare con la configurazione in modo più naturale. I limiti sono che non è possibile utilizzare il IOptions<T> pattern, in modo da non avere la possibilità di ricaricare la configurazione senza riavviare l'applicazione. Per quanto onestamente questa sia una cosa che non voglio quasi mai fare.

Il codice

Nella mia versione uso i recenti membri statici di Interface per specificare che tutti i casi di questa classe devono dichiarare un Section proprieta'. Questo viene usato per ottenere la sezione dalla configurazione.

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

Quindi per ogni implementazione si specifica a quale sezione questo dovrebbe essere vincolato:

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

In questo caso è alla ricerca di una sezione nella configurazione chiamata Newsletter.

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

Noi saremo poi in grado di legare questo nel Program.cs file in questo modo:


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

Possiamo anche ottenere il valore della configurazione nel Program.cs file in questo modo:

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

O anche per il WebApplicationBuilder così:

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

Nota: Poiché il costruttore ha accesso alla ConfigurationManager Non c'e' bisogno di farlo passare.

Significa che ora hai delle opzioni su come legare la config.

Un altro vantaggio è che se avete bisogno di utilizzare questi valori in seguito nel vostro Program.cs quindi l'oggetto è disponibile per voi.

Il metodo di estensione

Per abilitare tutto questo abbiamo un metodo di estensione abbastanza semplice che fa il lavoro di vincolare la configurazione alla classe.

Il codice qui sotto permette quanto segue:

// 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>();

Tutto questo è abilitato dalla seguente classe di estensione.

Potete vedere che l'impulso principale di questo è l'utilizzo dei membri dell'interfaccia statica per specificare il nome della sezione. Questo viene poi usato per ottenere la sezione dalla configurazione.

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

In uso

Usare questi è abbastanza semplice. In qualsiasi classe in cui hai bisogno di questa configurazione puoi semplicemente iniettarla in questo modo:

public class NewsletterService(NewsletterConfig config {
 
}

In conclusione

Beh, è... abbastanza semplice, ma è una tecnica che uso in tutti i miei progetti. E 'un bel modo di lavorare con la configurazione e penso che è un po 'più naturale IOptions<T> schema.

logo

©2024 Scott Galloway