Antecedentes
Nuestra empresa, entre otras cosas, ha desarrollado varios servicios (más precisamente, 12) que funcionan como el back-end de nuestros sistemas. Cada uno de los servicios es un servicio de Windows y realiza sus tareas específicas.
Me gustaría transferir todos estos servicios a * nix-OS. Para hacer esto, abandone el contenedor en forma de servicios de Windows y cambie de .NET Framework a .NET Standard.
El último requisito lleva a la necesidad de deshacerse de algunos códigos heredados, que no son compatibles con .NET Standard, incluidos desde el soporte para configurar nuestros servidores a través de XML, implementado usando clases de System.Configuration. Al mismo tiempo, esto resuelve el problema de larga data relacionado con el hecho de que en las configuraciones XML cometimos errores de vez en cuando al cambiar la configuración (por ejemplo, a veces colocamos la etiqueta de cierre en el lugar incorrecto o la olvidamos), pero es un lector maravilloso de las configuraciones XML de System.Xml. XmlDocument se traga silenciosamente tales configuraciones, produciendo un resultado completamente impredecible.
Se decidió cambiar a la configuración a través del moderno YAML. ¿Qué problemas enfrentamos y cómo los resolvimos? En este artículo.
Que tenemos
¿Cómo leemos la configuración de XML?
Leemos XML de manera estándar para la mayoría de los otros proyectos.
Cada servicio tiene un archivo de configuración para proyectos .NET, llamado AppSettings.cs, que contiene todas las configuraciones requeridas por el servicio. Algo como esto:
[System.Configuration.SettingsProvider(typeof(PortableSettingsProvider))] internal sealed partial class AppSettings : IServerManagerConfigStorage, IWebSettingsStorage, IServerSettingsStorage, IGraphiteAddressStorage, IDatabaseConfigStorage, IBlackListStorage, IKeyCloackConfigFilePathProvider, IPrometheusSettingsStorage, IMetricsConfig { }
Una técnica similar para separar configuraciones en interfaces hace que sea conveniente usarlas más tarde a través de un contenedor DI.
Toda la magia principal de almacenar configuraciones está realmente oculta en PortableSettingsProvider (vea el atributo de clase), así como en el archivo de diseñador AppSettings.Designer.cs:
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] internal sealed partial class AppSettings : global::System.Configuration.ApplicationSettingsBase { private static AppSettings defaultInstance = ((AppSettings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new AppSettings()))); public static AppSettings Default { get { return defaultInstance; } } [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("35016")] public int ListenPort { get { return ((int)(this["ListenPort"])); } set { this["ListenPort"] = value; } } ...
Como puede ver, "detrás de escena" están ocultas todas esas propiedades que agregamos a la configuración del servidor cuando la editamos a través del diseñador de configuraciones en Visual Studio.
Nuestra clase PortableSettingsProvider, mencionada anteriormente, lee directamente el archivo XML, y el resultado de la lectura ya se usa en SettingsProvider para escribir configuraciones en las propiedades de AppSettings.
Un ejemplo de la configuración XML que estamos leyendo (la mayoría de las configuraciones están ocultas por razones de seguridad):
<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup"> <section name="MetricServer.Properties.Settings" type="System.Configuration.ClientSettingsSection" /> </sectionGroup> </configSections> <userSettings> <MetricServer.Properties.Settings> <setting name="MCXSettings" serializeAs="String"> <value>Inactive, ChartLen: 1000, PrintLen: 50, UseProxy: False</value> </setting> <setting name="KickUnknownAfter" serializeAs="String"> <value>00:00:10</value> </setting> ... </MetricServer.Properties.Settings> </userSettings> </configuration>
Qué archivos de YAML me gustaría leer
Algo como esto:
VirtualFeed: MaxChartHistoryLength: 10 Port: 35016 UseThrottling: True ThrottlingIntervalMs: 50000 UseHistoryBroadcast: True CalendarName: "EmptyCalendar" UsMarketFeed: UseImbalances: True
Problemas de transición
En primer lugar, las configuraciones en XML son "planas", pero en YAML no lo son (se admiten secciones y subsecciones). Esto se ve claramente en los ejemplos anteriores. Usando XML, resolvimos el problema de la configuración plana al introducir nuestros propios analizadores que pueden convertir cadenas de cierto tipo en nuestras clases más complejas. Un ejemplo de una cadena tan compleja:
<setting name="MCXSettings" serializeAs="String"> <value>Inactive, ChartLen: 1000, PrintLen: 50, UseProxy: False</value> </setting>
No tengo ganas de hacer esas transformaciones cuando trabajo con YAML. Pero al mismo tiempo, estamos limitados por la estructura "plana" existente de la clase AppSettings: todas las propiedades de la configuración en ella se acumulan en un montón.
En segundo lugar, las configuraciones de nuestros servidores no son un monolito estático, las cambiamos de vez en cuando en el transcurso del trabajo del servidor, es decir. estos cambios deben ser capaces de capturar sobre la marcha, en tiempo de ejecución. Para hacer esto, en la implementación XML, heredamos nuestros AppSettings de INotifyPropertyChanged (de hecho, cada interfaz que implementa AppSettings se hereda de él) y nos suscribimos para actualizar los eventos de configuración de propiedades. Este enfoque funciona porque la clase base System.Configuration.ApplicationSettingsBase lista para usar implementa INotifyPropertyChanged. Se debe mantener un comportamiento similar después de la transición a YAML.
En tercer lugar, en realidad no tenemos un archivo de configuración para cada servidor, sino dos: uno con la configuración predeterminada y el otro con los anulados. Esto es necesario para que en cada una de las varias instancias de servidores del mismo tipo, que escuchen puertos diferentes y tengan configuraciones ligeramente diferentes, no tenga que copiar completamente todo el conjunto de configuraciones.
Y un problema más : el acceso a la configuración pasa no solo a través de las interfaces, sino también a través del acceso directo a AppSettings.Default. Permíteme recordarte cómo se declara en el Backstage AppSettings.Designer.cs:
private static AppSettings defaultInstance = ((AppSettings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new AppSettings()))); public static AppSettings Default { get { return defaultInstance; } }
Basado en lo anterior, fue necesario idear un nuevo enfoque para almacenar la configuración en AppSettings.
Solución
Kit de herramientas
Para la lectura directa, YAML decidió usar bibliotecas listas para usar disponibles a través de NuGet:
- YamlDotNet - github.com/aaubry/YamlDotNet . De la descripción de la biblioteca (traducción):
YamlDotNet es la biblioteca .NET para YAML. YamlDotNet proporciona un analizador de bajo nivel y un generador de YAML, así como un modelo de objetos de alto nivel similar a XmlDocument. También se incluye una biblioteca de serialización que le permite leer y escribir objetos desde / hacia secuencias YAML.
- NetEscapades.Configuration - github.com/andrewlock/NetEscapades.Configuration . Este es el proveedor de configuración en sí (en el sentido de Microsoft.Extensions.Configuration.IConfigurationSource, que se usa activamente en las aplicaciones ASP.NET Core), que lee archivos YAML utilizando solo el mencionado anteriormente YamlDotNet.
Lea más sobre cómo usar estas bibliotecas
aquí .
Transición a YAML
La transición en sí se realizó en dos etapas: al principio, simplemente cambiamos de XML a YAML, pero conservamos una jerarquía plana de archivos de configuración, y luego ingresamos secciones en archivos YAML. Estas etapas podrían, en principio, combinarse en una sola, y por simplicidad de presentación haré exactamente eso. Todas las acciones descritas a continuación se aplicaron secuencialmente a cada servicio.
Preparando un archivo YML
Primero debes preparar el archivo YAML. Lo llamamos el nombre del proyecto (útil para futuras pruebas de integración, que deberían poder trabajar con diferentes servidores y distinguir sus configuraciones entre ellos), colocar el archivo directamente en la raíz del proyecto, junto a AppSettings:

En el archivo YML, para empezar, guardemos una estructura "plana":
VirtualFeed: "MaxChartHistoryLength: 10, UseThrottling: True, ThrottlingIntervalMs: 50000, UseHistoryBroadcast: True, CalendarName: EmptyCalendar" VirtualFeedPort: 35016 UsMarketFeedUseImbalances: True
Relleno de ajustes de aplicaciones con propiedades de configuración
Transferimos todas las propiedades de AppSettings.Designer.cs a AppSettings.cs, eliminando simultáneamente los atributos superfluos del diseñador y el código en sí mismo en get / set-parts.
Fue:
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("35016")] public int VirtualFeedPort{ get { return ((int)(this["VirtualFeedPort"])); } set { this["VirtualFeedPort"] = value; } }
Se convirtió en:
public int VirtualFeedPort { get; set; }
Eliminaremos completamente AppSettings
.Designer .cs como innecesario. Ahora, por cierto, puede deshacerse por completo de la sección UserSettings en el archivo app.config, si está en el proyecto; allí se almacenan las mismas configuraciones predeterminadas, que especificamos a través del diseñador de configuraciones.
Adelante
Configuraciones de control sobre la marcha
Dado que necesitamos poder obtener actualizaciones de nuestra configuración en tiempo de ejecución, necesitamos implementar INotifyPropertyChanged en nuestros AppSettings. La base System.Configuration.ApplicationSettingsBase ya no está allí, respectivamente, no puedes contar con ninguna magia.
Puede implementarlo "en la frente": agregando una implementación de un método que arroje el evento deseado y llamándolo en el setter de cada propiedad. Pero estas son líneas de código adicionales, que además deberán copiarse en todos los servicios.
Hagámoslo mejor: introduzca un AutoNotifier de clase base auxiliar, que en realidad hace lo mismo, pero detrás de escena, al igual que System.Configuration.ApplicationSettingsBase hizo antes:
Aquí, el atributo [CallerMemberName] le permite obtener automáticamente el nombre de la propiedad del objeto que llama, es decir AppSettings
Ahora podemos heredar nuestros AppSettings de este AutoNotifier de clase base, y luego cada propiedad se modifica ligeramente:
public int VirtualFeedPort { get { return Get<int>(); } set { Set(value); } }
Con este enfoque, nuestras clases AppSettings, que incluso contienen una gran cantidad de configuraciones, se ven compactas y, al mismo tiempo, implementan INotifyPropertyChanged por completo.
Sí, sé que sería posible introducir un poco más de magia, usando, por ejemplo, Castle.DynamicProxy.IInterceptor, interceptando cambios en las propiedades necesarias y generando eventos allí. Pero esa decisión me pareció demasiado sobrecargada.
Configuración de lectura de un archivo YAML
El siguiente paso es agregar el lector de la configuración de YAML. Esto sucede en algún lugar más cercano al inicio del servicio. Al ocultar detalles innecesarios que no están relacionados con el tema en discusión, obtenemos algo similar:
public static IServerConfigurationProvider LoadServerConfiguration(IReadOnlyDictionary<Type, string> allSections) { IConfigurationBuilder builder = new ConfigurationBuilder().SetBasePath(ConfigFiles.BasePath); foreach (string configFile in configFiles) { string directory = Path.GetDirectoryName(configFile); if (!string.IsNullOrEmpty(directory))
En el código presentado, ConfigurationBuilder probablemente no sea de especial interés: todo el trabajo con él es similar al trabajo con configuraciones en ASP.NET Core. Pero los siguientes puntos son de interés. En primer lugar, "fuera de la caja" también tuvimos la oportunidad de combinar configuraciones de varios archivos. Esto proporciona el requisito de tener al menos dos archivos de configuración por servidor, como mencioné anteriormente. En segundo lugar, pasamos toda la configuración de lectura a un determinado ServerConfigurationProvider. Por qué
Secciones en el archivo YAML
Responderemos esta pregunta más tarde, y ahora volveremos al requisito de almacenar configuraciones estructuradas jerárquicamente en un archivo YML.
En principio, implementar esto es bastante simple. Primero, en el archivo YML, presentamos la estructura que necesitamos:
VirtualFeed: MaxChartHistoryLength: 10 Port: 35016 UseThrottling: True ThrottlingIntervalMs: 50000 UseHistoryBroadcast: True CalendarName: "EmptyCalendar" UsMarketFeed: UseImbalances: True
Ahora vamos a AppSettings y enséñele cómo dividir nuestras propiedades en secciones. Algo como esto:
public sealed class AppSettings : AutoNotifier, IWebSettingsStorage, IServerSettingsStorage, IServerManagerAddressStorage, IGlobalCredentialsStorage, IGraphiteAddressStorage, IDatabaseConfigStorage, IBlackListStorage, IKeyCloackConfigFilePathProvider, IPrometheusSettingsStorage, IHeartBeatConfig, IConcurrentAcceptorProperties, IMetricsConfig { public static IReadOnlyDictionary<Type, string> Sections { get; } = new Dictionary<Type, string> { {typeof(IDatabaseConfigStorage), "Database"}, {typeof(IWebSettingsStorage), "Web"}, {typeof(IServerSettingsStorage), "Server"}, {typeof(IConcurrentAcceptorProperties), "ConcurrentAcceptor"}, {typeof(IGraphiteAddressStorage), "Graphite"}, {typeof(IKeyCloackConfigFilePathProvider), "Keycloak"}, {typeof(IPrometheusSettingsStorage), "Prometheus"}, {typeof(IHeartBeatConfig), "Heartbeat"}, {typeof(IServerManagerAddressStorage), "ServerManager"}, {typeof(IGlobalCredentialsStorage), "GlobalCredentials"}, {typeof(IBlackListStorage), "Blacklist"}, {typeof(IMetricsConfig), "Metrics"} }; ...
Como puede ver, agregamos un diccionario directamente a AppSettings, donde las claves son los tipos de interfaces que implementa la clase AppSettings, y los valores son los encabezados de las secciones correspondientes. Ahora podemos comparar la jerarquía en el archivo YML con la jerarquía de propiedades en AppSettings (aunque no es más profundo que un nivel de anidamiento, pero en nuestro caso esto fue suficiente).
¿Por qué estamos haciendo esto aquí, en AppSettings? Porque de esta manera no difundimos la información sobre la configuración de diferentes entidades, y además, este es el lugar más natural, porque en cada servicio y, en consecuencia, en cada AppSettings, su propia sección de configuración.
Si no necesita una jerarquía en la configuración?
En principio, es un caso extraño, pero lo tuvimos exactamente en la primera etapa, cuando simplemente cambiamos de XML a YAML, sin usar las ventajas de YAML.
En este caso, esta lista completa de secciones no se puede almacenar, y ServerConfigurationProvider será mucho más simple (discutido más adelante).
Pero el punto importante es que si decidimos dejar una jerarquía plana, entonces podemos cumplir con el requisito de mantener la capacidad de acceder a la configuración a través de AppSettings.Default. Para hacer esto, agregue aquí un constructor público tan simple en AppSettings:
public static AppSettings Default { get; } public AppSettings() { Default = this; }
Ahora podemos continuar accediendo a la clase de configuración en todas partes a través de AppSettings.Default (siempre que la configuración ya se haya leído a través de IConfigurationRoot en ServerConfigurationProvider y, en consecuencia, AppSettings se haya instanciado).
Si una jerarquía plana es inaceptable, entonces, de todos modos, debe deshacerse de AppSettings. Por defecto en todas partes por código y trabajar con la configuración solo a través de interfaces (lo cual es bueno en principio). Por qué es así, quedará claro aún más.
ServerConfigurationProvider
La clase especial ServerConfigurationProvider mencionada anteriormente trata con la misma magia que le permite trabajar completamente con la nueva configuración jerárquica YAML con solo un AppSettings plano.
Si no puedes esperar, aquí está.
Código de proveedor de configuración de servidor completo ServerConfigurationProvider está parametrizado por la clase de configuración de AppSettings:
public class ServerConfigurationProvider<TAppSettings> : IServerConfigurationProvider where TAppSettings : new()
Esto, como puede suponer, le permite usarlo de inmediato en todos los servicios.
La configuración de lectura en sí (IConfigurationRoot), así como el diccionario de sección mencionado anteriormente (AppSettings.Sections) se pasan al constructor. Hay una suscripción a las actualizaciones de archivos (¿queremos enviarnos inmediatamente estos cambios en caso de un cambio en el archivo YML?):
_configuration.GetReloadToken()?.RegisterChangeCallback(OnConfigurationFileChanged, null); ... private void OnConfigurationFileChanged(object _) { foreach (string sectionName in _cachedSections.Keys) { Type sectionInterface = _interfacesBySections[sectionName]; TAppSettings newSection = ReadSection(sectionName, sectionInterface); TAppSettings oldSection; if (_cachedSections.TryGetValue(sectionName, out oldSection)) { UpdateSection(oldSection, newSection); } } }
Como puede ver, aquí, en caso de actualizar el archivo YML, revisamos todas las secciones que conocemos y leemos cada una. Luego, si la sección ya se ha leído anteriormente en la memoria caché (es decir, ya fue solicitada en alguna parte del código por alguna clase), reescribimos los valores antiguos en la memoria caché con valores nuevos.
Parecería: ¿por qué leer cada sección, por qué no leer solo las que están en el caché (es decir, exigidas)? Porque al leer la sección hemos implementado una verificación para la configuración correcta. Y en caso de configuración incorrecta, se descartan las alertas correspondientes y se registran los problemas. Es mejor aprender sobre los problemas en los cambios de configuración lo antes posible, de los cuales leemos todas las secciones de inmediato.
Actualizar valores antiguos en el caché con valores nuevos es bastante trivial:
private void UpdateSection(TAppSettings oldConfig, TAppSettings newConfig) { foreach (PropertyInfo propertyInfo in typeof(TAppSettings).GetProperties().Where(p => p.GetMethod != null && p.SetMethod != null)) { propertyInfo.SetValue(newConfig, propertyInfo.GetValue(oldConfig)); } }
Pero leer secciones no es tan simple:
private TAppSettings ReadSection(string sectionName, Type sectionInterface) { TAppSettings parsed; try { IConfigurationSection section = _configuration.GetSection(sectionName); CheckSection(section, sectionName, sectionInterface); parsed = section.Get<TAppSettings>(); if (parsed == null) {
Aquí, en primer lugar, leemos la sección en sí usando el IConfigurationRoot.GetSection estándar.
Luego solo verifique la corrección de la sección de lectura.A continuación, leemos la sección bindim para el tipo de nuestra configuración: sección. Aquí tenemos una característica del analizador YAML: no distingue entre una sección vacía (sin parámetros, es decir, ausente) de una sección en la que todos los parámetros están vacíos.Aquí hay un caso similar: VirtualFeed: Names: []
Aquí en la sección VirtualFeed hay un parámetro de Nombres con una lista vacía de valores, pero el analizador YAML, desafortunadamente, dirá que la sección VirtualFeed generalmente está completamente vacía. Es triste
Y finalmente, en este método se implementa un poco de magia callejera para admitir propiedades IEnumerable en la configuración. No logramos lograr una lectura normal de las listas "fuera de la caja". ReadArrays(parsed, section); ...
Como puede ver, encontramos todas las propiedades cuyo tipo se hereda de IEnumerable y les asignamos valores desde la "sección" ficticia, también nombrada como la configuración que nos interesa. Pero antes de eso, no olvide verificar: ¿hay un valor anulado de esta propiedad enumerada en el segundo archivo de configuración? Si lo hay, solo lo tomamos y borramos la configuración leída del archivo de configuración base. Si no se hace esto, ambas propiedades (del archivo base y del archivo anulado) se fusionarán automáticamente en una matriz en el nivel IConfigurationSection, y los índices de la matriz servirán como claves para la combinación. Esto dará como resultado algún tipo de hash en lugar del valor anulado normal.El método ReadSection que se muestra se utiliza en última instancia en el método principal de la clase: FindSection. [CanBeNull] public object FindSection(Type sectionInterface) { string sectionName = FindSectionName(sectionInterface); if (sectionName == null) { return null; }
En principio, queda claro por qué, con el soporte de las secciones, no podemos admitir AppSettings. De forma predeterminada: cada acceso a una nueva sección de configuración (previamente no leída) a través de FindSection realmente nos dará una nueva instancia de la clase AppSettings, aunque esté conectada a la interfaz deseada y, en consecuencia, si usáramos AppSettings.Default, se redefiniría cada vez que se leyera una nueva sección y contendría solo las configuraciones que pertenecen a la última sección leída (el resto tendría valores predeterminados: NULL y 0).La validación de la configuración en la sección se implementa de la siguiente manera: private void CheckSection(IConfigurationSection section, string sectionName, Type sectionInterface) { ICollection<PropertyInfo> properties = GetPublicProperties(sectionInterface, needSetters: false); var configProperties = new HashSet<string>(section.GetChildren().Select(c => c.Key)); foreach (PropertyInfo propertyInfo in properties) { if (!configProperties.Remove(propertyInfo.Name)) { if (propertyInfo.PropertyType != typeof(string) && typeof(IEnumerable).IsAssignableFrom(propertyInfo.PropertyType)) {
Aquí, en primer lugar, se extraen todas las propiedades públicas de la interfaz que nos interesa (secciones de configuración de lectura). Y para cada una de estas propiedades se encuentra una coincidencia en la configuración de lectura: si no se encuentra una coincidencia, se registra el problema correspondiente, porque esto significa que falta alguna configuración en el archivo de configuración. Al final, se verifica adicionalmente si alguna de las configuraciones de lectura permaneció incomparable con la interfaz. Si hay alguno, entonces el problema se registra nuevamente, porque Esto significa que las propiedades que no se describen en la interfaz se encontraron en el archivo de configuración, que tampoco debería estar en una situación normal.Surge la pregunta: ¿de dónde viene el requisito, que en el archivo de lectura todas las configuraciones deben corresponder a las disponibles en la interfaz de forma individual? El hecho es que, de hecho, como se mencionó anteriormente, en ese momento no se leyó un archivo, sino dos a la vez, uno con la configuración predeterminada y el otro con los anulados, y ambos son contiguos. En consecuencia, de hecho, no estamos mirando la configuración de un archivo, sino los completos. Y en este caso, por supuesto, su conjunto debe corresponder a los ajustes esperados uno a uno.También preste atención en las fuentes anteriores al método GetPublicProperties, que, al parecer, solo devuelve todas las propiedades públicas de la interfaz. Pero no es tan simple como podría ser, porque a veces tenemos una interfaz que describe la configuración del servidor que se hereda de otra interfaz y, en consecuencia, es necesario observar toda la jerarquía de interfaces para encontrar todas las propiedades públicas.Obtener la configuración del servidor
En base a lo anterior, para obtener la configuración del servidor en todas partes por código, recurrimos a la siguiente interfaz:
El primer método de esta interfaz, FindSection, le permite acceder a la sección de configuración de interés. Algo como esto:
IThreadPoolProperties threadPoolProperties = ConfigurationProvider.FindSection<IThreadPoolProperties>();
¿Por qué se necesitan el segundo y el tercer método? Explicaré más a fondo.Registro de interfaces de configuración
En nuestro proyecto, Castle Windsor se utiliza como contenedor de IoC. Es él quien suministra, incluidas las interfaces de configuración del servidor. En consecuencia, estas interfaces deben estar registradas en él.Para este propósito, se escribió una clase de extensión simple, que simplifica este procedimiento para no escribir el registro de todo el conjunto de interfaces en cada servidor: public static class ServerConfigurationProviderExtensions { public static void RegisterAllConfigurationSections(this IWindsorContainer container, IServerConfigurationProvider configurationProvider) { Register(container, configurationProvider, configurationProvider.AllSections.ToArray()); } public static void Register(this IWindsorContainer container, IServerConfigurationProvider configurationProvider, params Type[] configSections) { var registrations = new IRegistration[configSections.Length]; for (int i = 0; i < registrations.Length; i++) { Type configSection = configSections[i]; object section = configurationProvider.FindSection(configSection); registrations[i] = Component.For(configSection).Instance(section).Named(configSection.FullName); } container.Register(registrations); } }
El primer método le permite registrar todas las secciones de configuración (para esto necesita la propiedad AllSections en la interfaz IServerConfigurationProvider).Y el segundo método se usa en el primero, y lee automáticamente la sección de configuración especificada utilizando nuestro ServerConfigurationProvider, escribiéndolo inmediatamente en la caché de ServerConfigurationProvider y registrándolo en Windsor.Es aquí donde se utiliza el segundo método FindSection, no parametrizado, de IServerConfigurationProvider.Todo lo que queda es llamar a nuestro método de Extensión en el código de registro del contenedor Windsor: container.RegisterAllConfigurationSections(configProvider);
Conclusión
Que paso
De la manera presentada, fue posible transferir sin problemas todas las configuraciones de nuestros servidores de XML a YAML, mientras se realizaban un mínimo de cambios en el código del servidor existente.Las configuraciones de YAML, a diferencia de XML, resultaron ser más legibles debido no solo a una mayor concisión, sino también a la compatibilidad con la partición.No inventamos nuestras propias bicicletas para analizar YAML, sino que utilizamos soluciones listas para usar. Sin embargo, para integrarlos en las realidades de nuestro proyecto, se requirieron algunos de los trucos descritos en este artículo. Espero que sean útiles para los lectores.Era posible conservar la capacidad de detectar cambios en la configuración de los bozales web de nuestros servidores sobre la marcha. Además, como beneficio adicional, también fue posible detectar cambios en el archivo YAML sobre la marcha (anteriormente, era necesario reiniciar el servidor para cualquier cambio en los archivos de configuración).Conservamos la capacidad de fusionar dos archivos de configuración: la configuración predeterminada y la anulada, y lo hicimos utilizando soluciones de terceros listas para usar.Lo que no funcionó muy bien
Tuve que abandonar la capacidad previamente disponible para guardar los cambios aplicados desde las caras web de nuestros servidores a los archivos de configuración, porque el soporte para dicha funcionalidad requeriría grandes gestos, y la tarea comercial que teníamos ante nosotros en general no era tal.Bueno, también tuve que rechazar el acceso a la configuración a través de AppSettings.Default, pero esto es más un plus que un minus.