# Custom Konfigurationssektionens förlängningar

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

# Inleiding

Det verkar som att alla har en version av den här koden. [Filip W](https://www.strathweb.com/2016/09/strongly-typed-configuration-in-asp-net-core-without-ioptionst/) och nyligen har min gamla kollega Phil Haack [hans version](https://haacked.com/archive/2024/07/18/better-config-sections/).

Bara för att färdigställa så här gör jag det.

[TOC]

# Varför?

Varför är det lättare att jobba med konfigurationen. Just nu är det första sättet `IOptions<T>` och `IOptionsSnapshot<T>` men det är lite svårt att jobba med den.
Begränsningarna är att du inte kan använda `IOptions<T>` mönster, så du inte får förmågan att ladda om konfigurationen utan att starta applikationen igen . men ärligt talat är det något jag nästan aldrig vill göra

# Koden

I min version använder jag de senaste statiska gränssnittmedlemmarna för att specifikera att alla instanser i denna klass måste `Section` property. Detta används för att få sektionen från konfigurationen

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

Så för varje implementering så specifikerar du vilken sektion den ska vara sammansluten till

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

I det här fallet tittar ' på en sektion i konfigurationen som kallas `Newsletter`.

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

```

Vi kommer då kunna binda detta i `Program.cs` file like so:

```csharp

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

```

Vi kan också få värdet på konfigurationen i `Program.cs` file like so:

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

```

Eller till och med `WebApplicationBuilder` som så

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

Nota: Eftersom byggaren har tillgång till `ConfigurationManager` det behöver inte passa in i

Det betyder att du nu har möjligheter att binda config.

En annan fördel är att om du behöver använda dessa värden senare i ditt `Program.cs` så är objektet tillgängligt för dig.

# Förlängningsmetoden

För att göra allt detta har vi en ganska enkel förlängningsmetod som gör jobbet med att binda konfigurationen till klass.

Koden härunder tillåter följande:

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

```

Allt detta är aktiverat av den här rozszerzensklassen.

Man kan se att den huvudsakliga grejen i detta är att använda de statiska gränssnittmedlemmarna för att definiera sektionens namn. Detta används sedan för att få ut sektionen från konfigurationen

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

# I användning

Att använda dessa är ganska enkelt. I alla klasser där du behöver den här konfigurationen kan du helt enkelt injicera den så

```csharp
public class NewsletterService(NewsletterConfig config {
 
}
```

# I slutsats

Tja, det är "'", det är ... , ganska enkelt men det är en teknik som jag använder i alla mina projekt. Det är ett bra sätt att jobba med konfigurationer och jag tror att "'" är lite mer naturligt än `IOptions<T>` mönster.