Una pr谩ctica habitual en desarrollos asp net es utilizar el fichero de configuraci贸n (o el app config en aplicativos de escritorio) como almac茅n de valores configurables que luego se utilizar谩n en el aplicativo para uno u otro prop贸sito.
Lo que en principio se nos antoja como una buena pr谩ctica, en la mayor铆a de las ocasiones, debido a una utilizaci贸n mal estructurada del fichero de configuraci贸n, se convierte en una lista incontrolable y sin organizaci贸n alguna de entradas en el nodo appSettings.
Para evitar esto existe una soluci贸n alternativa, m谩s elegante, m谩s clara y, m谩s optima. Las clases ConfigurationSection, ConfigurationElement y ConfigurationElementCollection permiten definir secciones, elementos y colecciones de elementos en el fichero de configuraci贸n y utilizarlas como cualquier otro objeto en nuestro aplicativo.
Vamos a definir una peque帽a secci贸n en el fichero de configuraci贸n y comentamos un poquito c贸mo realizarla.
La clase EjemploData heredar谩 de ConfigurationElement y tendr谩 dos propiedades Name y PropiedadBooleana, de tipo string y bool respectivamente. Las propiedades de ambas deber谩n tener el atributo ConfigurationProperty con el nombre que tomar谩 en el fichero de configuraci贸n, su valor por defecto (si queremos especificarlo) y si es obligatoria.
public聽class聽EjemploData聽:聽ConfigurationElement 聽聽聽聽{ 聽聽聽聽聽聽聽聽#region聽Public聽Properties 聽聽聽聽聽聽聽聽private聽const聽string聽NamePropertyName聽=聽"name"; 聽聽聽聽聽聽聽聽[ConfigurationProperty(NamePropertyName,聽IsRequired聽=聽true)] 聽聽聽聽聽聽聽聽public聽string聽Name 聽聽聽聽聽聽聽聽{ 聽聽聽聽聽聽聽聽聽聽聽聽get聽{聽return聽this[NamePropertyName].ToString();聽} 聽聽聽聽聽聽聽聽聽聽聽聽set聽{聽this[NamePropertyName]聽=聽value;聽} 聽聽聽聽聽聽聽聽} 聽聽聽聽聽聽聽聽private聽const聽string聽PropiedadBooleanaPropertyName聽=聽"propiedadBooleana"; 聽聽聽聽聽聽聽聽[ConfigurationProperty(PropiedadBooleanaPropertyName,聽DefaultValue聽=聽"true",聽IsRequired聽=聽false)] 聽聽聽聽聽聽聽聽public聽bool聽PropiedadBooleana 聽聽聽聽聽聽聽聽{ 聽聽聽聽聽聽聽聽聽聽聽聽get聽{聽return聽(bool)this[PropiedadBooleanaPropertyName];聽} 聽聽聽聽聽聽聽聽聽聽聽聽set聽{聽this[PropiedadBooleanaPropertyName]聽=聽value;聽} 聽聽聽聽聽聽聽聽} 聽聽聽聽聽聽聽聽#endregion 聽聽聽聽}
Crearemos una clase EjemploDataCollection que herede de ConfigurationElementCollection, tal y como se muestra, no requiere mucha explicaci贸n
public聽class聽EjemploDataCollection聽:聽ConfigurationElementCollection { #region聽ConfigurationElementCollection聽Members protected聽override聽ConfigurationElement聽CreateNewElement() { return聽new聽EjemploData(); } protected聽override聽object聽GetElementKey(ConfigurationElement聽element) { return聽((EjemploData)element).Name; } public聽EjemploData聽this[int聽index] { get { return聽(EjemploData)BaseGet(index); } set { if聽(BaseGet(index)聽!=聽null) { BaseRemoveAt(index); } BaseAdd(index,聽value); } } #endregion }
Finalmente, crearemos la clase que nos permita manejar este tipo de objetos. Esta clase debe heredar de ConfigurationSection, la cual nos servir谩 para marcar el 谩mbito en el cual obtendremos los valores dentro del fichero de configuraci贸n. Debe tener:
- Una constante p煤blica que nos indique c贸mo llegar a la secci贸n adecuada del fichero de configuraci贸n.
- Constantes privadas que nos marquen c贸mo se llamar谩n los atributos.
- Nodos hijos que podremos definir en el fichero de configuraci贸n
- Sus respectivas propiedades p煤blicas que nos permitir谩n utilizarlas en cualquier parte de nuestro aplicativo.
En nuestro ejemplo definiremos la seccion como 鈥oxxigeno/ejemploSettings鈥, lo que significa que en el fichero de configuraci贸n tendremos que definir un nodo oxxigeno y dentro uno ejemploSettings.
Queremos que exista una atributo en el nodo ejemploSettings, para lo cual crearemos una constante privada que nos diga como debe llamarse, 鈥nuevaPropiedad鈥, y una propiedad p煤blica que nos permita recuperarlo del fichero de configuraci贸n y utilizarlo en el aplicativo. Para ello crearemos una propiedad normal que nos devolver谩 un objeto de tipo string y le establecemos un atributo a la propiedad de tipo ConfigurationProperty, que indique c贸mo se va a llamar ese atributo en el fichero de configuraci贸n y si es obligatorio o no que exista.
Adem谩s nos interesa que bajo el nodo ejemploSettings exista una colecci贸n de nodos del mismo tipo (en nuestro caso EjemploData). Para ello procederemos igual que para la propiedad actual, pero definiremos su salida como EjemploDataCollection, y le agregaremos el atributo ConfigurationCollection pas谩ndole como par谩metro el tipo de objetos que se coleccionar谩n.
public聽sealed聽class聽EjemploSettings聽:聽ConfigurationSection { public聽const聽string聽SectionName聽=聽"oxxigeno/ejemploSettings"; #region聽Public聽Properties private聽const聽string聽NuevaPropiedadPropertyName聽=聽"nuevaPropiedad"; [ConfigurationProperty(NuevaPropiedadPropertyName,聽IsRequired聽=聽true)] public聽string聽NuevaPropiedad { get聽{聽return聽this[NuevaPropiedadPropertyName].ToString();聽} set聽{聽this[NuevaPropiedadPropertyName]聽=聽value;聽} } private聽const聽string聽ColeccionDeEjemploPropertyName聽=聽"coleccionDeEjemplo"; [ConfigurationProperty(ColeccionDeEjemploPropertyName,聽IsRequired聽=聽true,聽IsDefaultCollection聽=聽true)] [ConfigurationCollection(typeof(EjemploData))] public聽EjemploDataCollection聽ColeccionDeEjemplo { get聽{聽return聽(EjemploDataCollection)this[ColeccionDeEjemploPropertyName];聽} } #endregion }
As铆 pues, para cargar datos en el fichero de configuraci贸n, debemos cargar la clase controladora (EjemploSettings) y montar el 谩rbol de nodos que hemos definido en las clases.
Dentro del nodo configurationSections debemos crear un nodo SectionGroup como se muestra, y dentro un nodo section. Pueden existir m煤ltiples nodos sectionGroup pero no con el mismo nombre. Cada nodo sectionGroup puede tener en su interior multiples nodos section.
<sectionGroup name="oxxigeno"> <section name="ejemploSettings" type="Oxxigeno.Ejemplo.Configuration.EjemploSettings, Oxxigeno.Ejemplo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=96df930e30970c00"> </sectionGroup>
Y聽despues聽creamos聽el聽谩rbol聽de聽nodos聽tal聽como聽lo聽hemos聽definido,聽quedar铆a聽algo聽as铆:
<oxxigeno> <ejemploSettings nuevaPropiedad="valorDeLaPropiedad"> <coleccionDeEjemplo> <clear/> <add name="nombreDeLaEntrada1" propiedadBooleana="false" /> <add name="nombreDeLaEntrada2" propiedadBooleana="true" /> </coleccionDeEjemplo> </ejemploSettings> </oxxigeno>
La forma de recuperar estos valores ser铆a la siguiente:
EjemploSettings聽ejemploSettings聽=聽ConfigurationManager.GetSection(EjemploSettings.SectionName)聽as聽EjemploSettings; string聽nuevaPropiedad聽=聽ejemploSettings.NuevaPropiedad; foreach聽(EjemploData聽obj聽in聽ejemploSettings.ColeccionDeEjemplo) { string聽nombre聽=聽obj.Name; bool聽propiedadBooleana聽=聽obj.PropiedadBooleana; }
Considero que este m茅todo de utilizaci贸n del fichero de configuraci贸n es:
- M谩s 煤til: Los valores se encuentran parseados al tipo necesario, no se obtienen unicamente valores de tipo string como pasa si utilizamos AppSettings, lo cual facilita el trabajo del desarrollador.
- M谩s estructurado: Los valores dentro del fichero de configuraci贸n son facilmente localizables y, por lo tanto, modificables.