NOTE: Apart from
(and even then it's questionable, I'm Scottish). These are machine translated in languages I don't read. If they're terrible please contact me.
You can see how this translation was done in this article.
Friday, 27 September 2024
//Less than a minute
Il semble que tout le monde a une version de ce code, j'ai d'abord rencontré cette approche de Filip W et récemment mon ancien collègue Phil Haack a sa version.
Juste pour terminer, c'est comme ça que je le fais.
Le pourquoi est de faciliter le travail avec la configuration. Pour le moment, le premier moyen est d'utiliser IOptions<T>
et IOptionsSnapshot<T>
Mais c'est un peu douloureux de travailler avec. Cette approche vous permet de travailler avec la configuration d'une manière plus naturelle.
Les limites sont que vous ne pouvez pas utiliser le IOptions<T>
modèle, de sorte que vous n'avez pas la possibilité de recharger la configuration sans redémarrer l'application. Cependant honnêtement, c'est quelque chose que je ne veux presque jamais faire.
Dans ma version, j'utilise les récents membres d'Interface statique pour spécifier que toutes les instances de cette classe doivent déclarer une Section
propriété. Ceci est utilisé pour obtenir la section de la configuration.
public interface IConfigSection {
public static abstract string Section { get; }
}
Donc, pour chaque mise en œuvre, vous spécifiez ensuite quelle section cela devrait être lié à:
public class NewsletterConfig : IConfigSection
{
public static string Section => "Newsletter";
public string SchedulerServiceUrl { get; set; } = string.Empty;
public string AppHostUrl { get; set; } = string.Empty;
}
Dans ce cas, il est à la recherche d'une section dans la configuration appelée Newsletter
.
"Newsletter": {
"SchedulerServiceUrl" : "http://localhost:5000",
"AppHostUrl" : "https://localhost:7240"
}
Nous serons alors en mesure de lier ceci dans le Program.cs
fichier comme ceci :
var builder = WebApplication.CreateBuilder(args);
var config = builder.Configuration;
services.ConfigurePOCO<NewsletterConfig>(config);
Nous pouvons également obtenir la valeur de la config dans le Program.cs
fichier comme ceci :
var newsletterConfig = services.ConfigurePOCO<NewsletterConfig>(config);
Ou même pour les WebApplicationBuilder
Comme ça :
var newsletterConfig = builder.Configure<NewsletterConfig>();
Remarque: Comme le constructeur a accès à la ConfigurationManager
Il n'est pas nécessaire que ça passe.
Ce qui signifie que vous avez maintenant des options sur la façon de lier config.
Un autre avantage est que si vous avez besoin d'utiliser ces valeurs plus tard dans votre Program.cs
fichier puis l'objet est disponible pour vous.
Pour activer tout cela, nous avons une méthode d'extension assez simple qui fait le travail de lier la configuration à la classe.
Le code ci-dessous permet:
// 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>();
Tout cela est activé par la classe d'extension suivante.
Vous pouvez voir que l'impulsion principale de ceci est d'utiliser les membres de l'interface statique pour spécifier le nom de la section. Ceci est ensuite utilisé pour obtenir la section de la configuration.
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);
}
}
Utiliser ceci est assez simple. Dans n'importe quelle classe où vous avez besoin de cette configuration, vous pouvez simplement l'injecter comme ceci:
public class NewsletterService(NewsletterConfig config {
}
Eh bien, c'est... plutôt simple, mais c'est une technique que j'utilise dans tous mes projets. C'est une belle façon de travailler avec la configuration et je pense que c'est un peu plus naturel que le IOptions<T>
modèle.