# Поширення секції налаштування

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

# Введение

Здається, що кожен має версію цього коду, Я вперше натрапив на цей підхід [Філіп В](https://www.strathweb.com/2016/09/strongly-typed-configuration-in-asp-net-core-without-ioptionst/) і нещодавно мій старий колега Фил Хак [його версію](https://haacked.com/archive/2024/07/18/better-config-sections/).

Просто для завершення ось як я це роблю.

[TOC]

# Чому?

Чому, щоб зробити роботу з конфігурацією легшою. На даний момент `IOptions<T>` і `IOptionsSnapshot<T>` але це трохи нелегко працювати з ним. Цей підхід дозволяє працювати з конфігурацією в більш природній спосіб
Ограничення полягає в тому, що ви не можете `IOptions<T>` шаблон, так що ви не маєте ' не отримуєте можливості перезавантаження конфігурації без перезапускання додаткуM SK2 Однак чесно кажучи, це те, чого я майже ніколи не хочу робити.

# Код

В моїй версії я використовую останні статичні члени інтерфейсу, щоб визначити, що всі instanції цієї класи повинні заявити `Section` property. Це використовується для отримання розділу з конфігурації

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

Отже, для кожного втілення ви визначаєте, яку секцію це має прив 'язати до:

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

В цьому випадку, it' шукає секцію в конфігурації, яка називається `Newsletter`.

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

```

Тоді ми зможемо зв 'язати це з `Program.cs` файл як так:

```csharp

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

```

Ми можемо також отримати цінність конфигурації в `Program.cs` файл як так:

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

```

Або навіть `WebApplicationBuilder` як так:

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

Nota: Так як будівник має доступ до `ConfigurationManager` це не обов 'язково має проходити в

Це означає, що тепер ви маєте можливість об 'єднати config.

Інша перевага в тому, що якщо вам потрібно використовувати ці цінності пізніше `Program.cs` тоді об 'єкт доступний вам.

# Metodа розширення

Для цього ми маємо досить просту методу розширення, яка виконує завдання прив 'язування конфігурації до класу.

Даний код дозволяє наступне:

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

```

Все це включено наступним класом розширення.

Ви бачите, що основним стимулом цього є використання статичних членів інтерфейсу для визначення назви секції. Потім це використовується для отримання секції з конфігураціїM SK1

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

# У використанні

Використовувати їх досить просто.. В будь-якому класі, де вам потрібна ця конфігурація, ви можете просто ввести її так.

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

# На завершення

Що ж, це дуже просто, але це методика, яку я використовую в усіх своїх проектах. Це хороший спосіб працювати з конфігурацією, і я думаю, що це трохи природніше, ніж `IOptions<T>` шаблон.