Back to "Aangepaste sectieextensies"

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#

Aangepaste sectieextensies

Friday, 27 September 2024

Inleiding

Het lijkt erop dat iedereen een versie van deze code heeft, ik kwam eerst deze benadering tegen van Filip W en onlangs heeft mijn oude collega Phil Haack zijn versie.

Gewoon voor de voltooiing dit is hoe ik het doe.

Waarom?

De reden is om het makkelijker te maken om te werken met configuratie. Op dit moment wordt de belangrijkste manier gebruikt IOptions<T> en IOptionsSnapshot<T> Maar dit is een beetje lastig om mee te werken. Deze aanpak stelt u in staat om op een meer natuurlijke manier met de configuratie te werken. De beperkingen zijn dat u de IOptions<T> patroon, zodat u niet de mogelijkheid krijgt om de configuratie te herladen zonder de toepassing te herstarten. Maar eerlijk gezegd is dit iets wat ik bijna nooit wil doen.

De code

In mijn versie gebruik ik de recente statische Interface-leden om aan te geven dat alle instanties van deze klasse een Section eigendom. Dit wordt gebruikt om de sectie uit de configuratie te krijgen.

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

Voor elke implementatie geef je dan aan aan aan welke sectie dit moet worden gebonden aan:

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

In dit geval is het op zoek naar een sectie in de configuratie genaamd Newsletter.

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

We zullen dan in staat zijn om dit te binden in de Program.cs bestand als volgt:


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

We kunnen ook de waarde van de config in de Program.cs bestand als volgt:

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

Of zelfs voor de WebApplicationBuilder Zoals:

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

Opmerking: Omdat de bouwer toegang heeft tot de ConfigurationManager Dat hoeft niet doorgegeven te worden.

Dat betekent dat je nu opties hebt om config te binden.

Een ander voordeel is dat als u deze waarden later in uw Program.cs bestand dan is het object beschikbaar voor u.

De extensiemethode

Om dit alles mogelijk te maken hebben we een vrij eenvoudige extensie methode die het werk van het binden van de configuratie aan de klasse doet.

De onderstaande code staat het volgende toe:

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

Dit is allemaal ingeschakeld door de volgende extensieklasse.

U kunt zien dat de belangrijkste impuls hiervan is het gebruik van de statische interface leden om de sectienaam op te geven. Dit wordt dan gebruikt om de sectie uit de configuratie te krijgen.

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 gebruik

Om deze te gebruiken is vrij eenvoudig. In elke klasse waar u deze configuratie nodig heeft, kunt u deze gewoon zo injecteren:

public class NewsletterService(NewsletterConfig config {
 
}

Conclusie

Nou dat is het... vrij eenvoudig maar het is een techniek die ik gebruik in al mijn projecten. Het is een leuke manier om te werken met configuratie en ik denk dat het is een beetje natuurlijker dan de IOptions<T> patroon.

logo

©2024 Scott Galloway