# Benutzerdefinierte Config-Sektionserweiterungen

<!--category-- C#, ASP.NET -->
<datetime class="hidden">2024-09-27T06:20</datetime>

# Einleitung

Es scheint, als hätte jeder eine Version dieses Codes, ich kam zuerst auf diesen Ansatz von [Filip W](https://www.strathweb.com/2016/09/strongly-typed-configuration-in-asp-net-core-without-ioptionst/) und kürzlich hat mein alter Kollege Phil Haack [seine Version](https://haacked.com/archive/2024/07/18/better-config-sections/).

Nur zum Abschluss mache ich das so.

[TOC]

# 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.

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

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

```csharp
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`.

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

```

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

```csharp

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:

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

```

Oder sogar für die `WebApplicationBuilder` wie so:

```csharp
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:

```csharp
// 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.

```csharp
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:

```csharp
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.