c# - existe - Fusionar secciones de configuración personalizadas en tiempo de ejecución en.NET
configurationmanager.appsettings c# (4)
Mi pregunta es sobre trabajar con objetos de configuración estándar de .NET y también elementos de configuración personalizados (lo que se puede definir al extender la clase System.Configuration.ConfigurationSection
). Usualmente los obtenemos de los métodos de la clase System.Configuration.ConfigurationManager
, siendo GetSection(...)
un ejemplo.
El objeto de configuración cargado parece ser un objeto de configuración fusionado que contiene la configuración presente en el archivo de configuración de la aplicación (el archivo app.config o web.config que un desarrollador puede haber creado) y lo que se define en el archivo machine.config (el este último viene con la instalación de .NET Framework).
Por lo tanto, podemos suponer que la configuración se carga de manera jerárquica con la máquina. Configuración primero, y cualquier configuración definida por el usuario que se superponga a la configuración predeterminada, y se podría ver de la siguiente manera:
- maquina.config
- app.config (anula / fusiona con los elementos respectivos que se encuentran en machine.config)
Mi objetivo es crear múltiples capas de configuración, de modo que pueda haber otros archivos de configuración entre (y, si es posible, después) la máquina.config y el archivo app.config :
- maquina.config
- custom.config (una configuración personalizada que interfiere entre machine.config y el archivo app.config)
- app.config - ahora app.config se fusiona con machine.config y custom.config
- custom.config (una configuración personalizada que interfiere entre machine.config y el archivo app.config)
Actualizar
El punto con eso es: si he definido la sección de configuración tanto en custom.config como en app.config , necesito obtener una versión combinada de ambas configuraciones cuando llamo a ConfigurationManager.GetSection("MyCustomSection")
. Idealmente, sería genial si puedo realizar la fusión como se describe en este artículo de MSDN .
Normalmente, escribiría mi propia clase de administrador de configuración e intentaría llegar lo más lejos posible para obtener el resultado deseado, pero asumiendo que el .NET framework funciona para machine.config y app.config , pensé que podría beneficiarme La funcionalidad incorporada del framework. Además, no sé cómo desencadenar manualmente tal fusión, si se supone que debo recurrir a una implementación del administrador de configuración por mi cuenta.
Entonces, ¿es posible aprovechar los mecanismos integrados de la sección / elemento de configuración que se fusiona con los archivos de configuración personalizados? Estoy especialmente interesado en desarrollar y admitir esto para las secciones de configuración personalizadas. El espacio de nombres System.Configuration
contiene objetos base para crear la sección y los elementos de configuración, y estos permiten algunas configuraciones relacionadas con la fusión (como, por ejemplo, establecer el ConfigurationElementCollectionType
apropiado). ¿Están relacionados con la fusión solo con machine.config (o varias capas de archivos web.config en una aplicación web), o es posible activar manualmente la fusión de archivos de configuración precargados? Estoy tratando de evitar tener que admitir la fusión personalizada en cualquiera de mis objetos de configuración personalizados y probablemente me olvide de admitir la configuración existente desde la Configuración del Sistema ...
Actualizar
Quisiera hacer una aclaración importante, en respuesta a las respuestas y comentarios existentes. Soy capaz de cargar los objetos de ConfigurationSection
desde la ConfigurationSection
de la aplicación actual ( app.config / web.config ) y desde un archivo físico que es mi custom.config . Necesito saber si existe la posibilidad de fusionar estos objetos sin recurrir a la reflexión y la comparación de propiedades por propiedades, por algún medio incorporado en el marco.
Nota: apreciaría mejor una solución que funcione en .NET 3.0+. Agregue una nota si su respuesta apunta a una versión superior del marco.
El sistema de configuración .NET no es super flexible.
Ese comentario lo aclara y explica por qué has estado buscando durante mucho tiempo y no has encontrado nada todavía. No todas las partes de .NET Framework son "buenas", System.Configuration merece el lugar en el extremo inferior. Está ridículamente diseñado en exceso para algo que, en última instancia, es una tarea simple, pero al mismo tiempo se convierte en algo extremadamente inflexible. Es difícil hacer ingeniería inversa sobre cómo sucedió esto, creo que se paralizó por problemas de seguridad. Algo comprensible tal vez, el control de un programa con datos siempre es un riesgo considerable.
El único punto de extensión que conozco es escribir su propio SettingsProvider . El marco tiene solo uno para uso general, la clase LocalFileSettingProvider. También extremadamente inflexible, no hay manera de alterar su comportamiento. Hay un ejemplo decente disponible para un proveedor de configuración personalizada, el ejemplo de RegistrySettingsProvider muestra un proveedor que almacena la configuración en el registro. Puede ser un buen punto de partida para escribir el tuyo.
Quizás no sea exactamente lo que tienes en mente, tacha la idea de que puedes entrar en la capa dentro de System.Configuration.
Como ha señalado, un ExeConfigurationFileMap
bien configurado podría hacer el trabajo, a un costo determinado.
He tomado su ejemplo y he hecho una versión funcional.
Aquí están los dos archivos de configuración que he fusionado para mis propósitos de prueba:
custom.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="custom" type="..." />
</configSections>
<custom>
<singleProperty id="main" value="BaseValue" />
<propertyCollection>
<property id="1" value="One" />
<property id="4" value="Four" />
</propertyCollection>
</custom>
</configuration>
app.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="custom" type="..."/>
</configSections>
<custom>
<singleProperty id="main" value="OverriddenValue" />
<propertyCollection>
<property id="1" value="OverridenOne" />
<property id="2" value="Two" />
<property id="3" value="Three" />
</propertyCollection>
</custom>
</configuration>
Y utilicé el siguiente código para probar la configuración combinada:
var map = new ExeConfigurationFileMap();
map.MachineConfigFilename = PathToCustomConfig;
map.ExeConfigFilename = PathToAppConfig;
var configuration = ConfigurationManager.OpenMappedExeConfiguration(
map,
ConfigurationUserLevel.None);
var section = configuration.GetSection("custom") as CustomConfigSection;
Assert.IsNotNull(section);
Assert.AreEqual(section.SingleProperty.Value, "OverriddenValue");
Assert.AreEqual(section.PropertyCollection.Count, 4);
// Needed to map the properties as dictionary, not to rely on the property order
var values = section.PropertyCollection
.Cast<SimpleConfigElement>()
.ToDictionary(x => x.ID, x => x.Value);
Assert.AreEqual(values["1"], "OverridenOne");
Assert.AreEqual(values["2"], "Two");
Assert.AreEqual(values["3"], "Three");
Assert.AreEqual(values["4"], "Four");
ventajas de este enfoque
- Obtengo la lógica de fusión incorporada para trabajar
- Funciona para versiones anteriores de .NET (probado en 3.5)
- No hay necesidad de reflexión u otras cosas de magia negra para desencadenar el comportamiento.
contras
- No muy seguro, pero configurando
map.MachineConfigFilename = PathToCustomConfig;
Supongo que estoy eliminando cualquier valor configurado por el archivo realmachine.config
. Esto podría ser propenso a errores y debería evitarse en las aplicaciones web, ya que la mayoría de ellas se basan en lo que hay en lamachine.config
real. - Uno debe pasar la ubicación del archivo de configuración de la aplicación, ya que ya no se determina automáticamente. Por lo tanto, uno necesita averiguar cómo se
app.config
cuando se compile el código (generalmente AssemblyName.exe.config) - Puede combinar el contenido de dos archivos únicos de esa manera. Si uno necesita una jerarquía más grande, esto no funcionará bien.
Todavía estoy en el proceso de perfeccionar la técnica, por lo tanto, volveré a actualizar esta publicación una vez que haya terminado.
De hecho, hay 3 niveles de herencia de configuración por defecto: Máquina, Exe y Usuario (que puede ser Roaming o Local). Si está cargando los archivos de configuración, puede usar la clase ExeConfigurationFileMap en combinación con ConfigurationManager.OpenMappedExeConfiguration para cargar su propia jerarquía de configuración personalizada.
No creo que pueda cambiar dónde están las rutas predeterminadas para la clase ConfigurationManager con esto, pero obtendrá un elemento de configuración que se puede usar para obtener las secciones de la jerarquía de configuración cargada.
Si verifica la respuesta a Cómo leer las configuraciones , incluye algunas notas para determinar a qué nivel se declararon las secciones en la jerarquía (usando la Información de la Sección)
var localSections = cfg.Sections.Cast<ConfigurationSection>()
.Where(s => s.SectionInformation.IsDeclared);
Eche un vistazo al siguiente código que sabe cómo cargar sus configuraciones o la configuración exe. Una vez que el siguiente código le quede claro, puede personalizar la carga y la fusión como desee (cargándola dos veces y anulando una con la otra).
private static void InitConfiguration()
{
var map = new ExeConfigurationFileMap();
var AssemblyConfigFile = "";
if (File.Exists(AssemblyConfigFile))
map.ExeConfigFilename = AssemblyConfigFile;
else
map.ExeConfigFilename = Path.Combine(Environment.CurrentDirectory, Environment.GetCommandLineArgs()[0]+".config");
var Configuration = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
var serviceModelSectionGroup = ServiceModelSectionGroup.GetSectionGroup(Configuration);
}