Back to "Benutzerdefinierte Config-Sektionserweiterungen"

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#

Benutzerdefinierte Config-Sektionserweiterungen

Friday, 27 September 2024

Einleitung

Es scheint, als hätte jeder eine Version dieses Codes, ich kam zuerst auf diesen Ansatz von Filip W und kürzlich hat mein alter Kollege Phil Haack seine Version.

Nur zum Abschluss mache ich das so.

Warum?

Der Grund ist, es einfacher zu machen, mit der Konfiguration zu arbeiten. Im Moment ist der erste Weg mit IOptions<T> und IOptionsSnapshot<T> aber das ist ein bisschen ein Schmerz, mit dem man arbeiten muss. Dieser Ansatz ermöglicht es Ihnen, mit der Konfiguration auf eine natürlichere Weise zu arbeiten. Die Einschränkungen sind, dass Sie nicht die IOptions<T> Muster, so dass Sie nicht die Möglichkeit erhalten, die Konfiguration neu zu laden, ohne die Anwendung neu zu starten. Aber ehrlich gesagt, das ist etwas, was ich fast nie tun will.

Der Code

In meiner Version nutze ich die letzten statischen Interface-Mitglieder, um festzulegen, dass alle Instanzen dieser Klasse eine Section Eigentum. Dies wird verwendet, um den Abschnitt aus der Konfiguration zu erhalten.

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

Für jede Implementierung geben Sie dann an, an welchen Abschnitt dies gebunden sein soll:

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

In diesem Fall sucht es einen Abschnitt in der Konfiguration namens Newsletter.

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

Wir werden dann in der Lage sein, dies in der Program.cs Datei wie so:


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

Wir können auch den Wert der Konfiguration in der Program.cs Datei wie so:

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

Oder sogar für die WebApplicationBuilder wie so:

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

Hinweis: Da der Bauherr Zugriff auf die ConfigurationManager Das muss nicht weitergegeben werden.

Das bedeutet, Sie haben jetzt Optionen, wie man config bindet.

Ein weiterer Vorteil ist, dass, wenn Sie diese Werte später in Ihrem Program.cs dann steht Ihnen das Objekt zur Verfügung.

Die Erweiterungsmethode

Um all dies zu ermöglichen, haben wir eine ziemlich einfache Erweiterungsmethode, die die Arbeit der Bindung der Konfiguration an die Klasse erledigt.

Der Code unten erlaubt Folgendes:

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

Dies ist alles durch die folgende Erweiterungsklasse aktiviert.

Sie können sehen, dass der Hauptimpuls darin ist, die statischen Schnittstellenelemente zu verwenden, um den Abschnittsnamen anzugeben. Dies wird dann verwendet, um den Abschnitt aus der Konfiguration zu erhalten.

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 Gebrauch

Diese zu verwenden ist ziemlich einfach. In jeder Klasse, in der Sie diese Konfiguration benötigen, können Sie sie einfach so injizieren:

public class NewsletterService(NewsletterConfig config {
 
}

Schlussfolgerung

Nun, das ist es... ziemlich einfach, aber es ist eine Technik, die ich in allen meinen Projekten benutze. Es ist eine schöne Art, mit Konfiguration zu arbeiten und ich denke, es ist ein bisschen natürlicher als die IOptions<T> Das ist das Muster.

logo

©2024 Scott Galloway