Back to "Нетипові розширення розділів налаштування"

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#

Нетипові розширення розділів налаштування

Friday, 27 September 2024

Вступ

Здається, що всі мають версію цього коду, я вперше натрапив на цей підхід з Філіп W і недавно мій колишній колега Філ Хак його версія.

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

Чому?

Чому можна полегшити роботу з налаштуваннями? В даний момент найбільш поширений спосіб використання IOptions<T> і IOptionsSnapshot<T> але з цим трохи важко працювати. За допомогою цього підходу ви зможете працювати з налаштуваннями у природніший спосіб. Межа полягає в тому, що ви не можете використовувати IOptions<T> шаблон, отже ви не зможете перезавантажити налаштування без перезапуску програми. Проте, чесно кажучи, я майже ніколи не хочу цього робити.

Код

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

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

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

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

У цьому випадку він шукає розділ у конфігурації під назвою Newsletter.

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

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


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

Ми також можемо отримати значення налаштувань у Program.cs файл, схожий на такий:

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

Або навіть для WebApplicationBuilder ось так:

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

Примітка: тому що будівничий має доступ до ConfigurationManager це не обов'язково приймати.

Тепер ви маєте параметри щодо прив' язки налаштувань.

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

Метод розширення

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

У коді, наведеному нижче, можна знайти такі рядки:

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

Це все можна увімкнути наступним класом розширення.

Ви можете бачити, що основним стимулом цього є використання елементів статичного інтерфейсу для визначення назви розділу. Цим пунктом можна скористатися для отримання розділу з налаштувань.

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

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

Користуватися ними досить просто. У будь- якому класі, де вам потрібні ці налаштування, ви можете просто ввести їх так:

public class NewsletterService(NewsletterConfig config {
 
}

Включення

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

logo

©2024 Scott Galloway