Back to "Extensiones de sección de configuración personalizadas"

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#

Extensiones de sección de configuración personalizadas

Friday, 27 September 2024

Introducción

Parece que todo el mundo tiene una versión de este código, por primera vez me encontré con este enfoque de Filip W y recientemente mi viejo colega Phil Haack ha su versión.

Sólo para completar así es como lo hago.

¿Por qué?

El porqué es hacer que sea más fácil trabajar con la configuración. Por el momento el camino principal está usando IOptions<T> y IOptionsSnapshot<T> Pero esto es un poco molesto para trabajar. Este enfoque le permite trabajar con la configuración de una manera más natural. Las limitaciones son que no se puede utilizar el IOptions<T> patrón, por lo que no consigue la capacidad de recargar la configuración sin reiniciar la aplicación. Sin embargo honestamente esto es algo que casi nunca quiero hacer.

El Código

En mi versión utilizo los miembros recientes de la interfaz estática para especificar que todas las instancias de esta clase deben declarar un Section propiedad. Esto se utiliza para obtener la sección de la configuración.

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

Así que para cada implementación usted entonces especifique a qué sección debe estar vinculado esto:

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

En este caso está buscando una sección en la configuración llamada Newsletter.

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

Entonces seremos capaces de atar esto en el Program.cs file like so:


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

También podemos obtener el valor de la configuración en el Program.cs file like so:

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

O incluso para el WebApplicationBuilder así:

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

Nota: Como el constructor tiene acceso a la ConfigurationManager No es necesario que eso pase.

Significa que ahora tienes opciones sobre cómo enlazar configuración.

Otra ventaja es que si usted necesita utilizar estos valores más adelante en su Program.cs archivo entonces el objeto está disponible para usted.

El método de extensión

Para habilitar todo esto tenemos un método de extensión bastante simple que hace el trabajo de vincular la configuración a la clase.

El siguiente código permite lo siguiente:

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

Todo esto está habilitado por la siguiente clase de extensión.

Puede ver que el impulso principal de esto es el uso de los miembros de la interfaz estática para especificar el nombre de la sección. Esto se utiliza para obtener la sección de la configuración.

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

En uso

Usar estos es bastante simple. En cualquier clase donde necesite esta configuración, simplemente puede inyectarla así:

public class NewsletterService(NewsletterConfig config {
 
}

Conclusión

Bueno, eso es... bastante simple, pero es una técnica que utilizo en todos mis proyectos. Es una buena manera de trabajar con la configuración y creo que es un poco más natural que el IOptions<T> patrón.

logo

©2024 Scott Galloway