Como armar configuraciones complejas en el archivo de configuración y trabajar con ellas

 

Fecha: 12/Ago/2005 (10/08/2005)
Autor: Gerardo Contijoch - mazarin9@yahoo.com.ar

 


Introducción

Cuando uno tiene que trabajar con el archivo de configuración de una aplicación .NET generalmente lo hace agregando tags "add" en la seccion "appConfig" del archivo de configuración. Esto es muy práctico hasta cierto punto. Puede que nuestra aplicación sea muy compleja o simplemente posea muchas opciones configurables y en estos casos es necesario crear un esquema de configuración más complejo. En este artículo vamos a ver como hacerlo.

Un ejemplo práctico

Supongamos que nuestra aplicación trabaja con una base de datos y con usuarios y que deseamos colocar en el archivo de configuración la cadena de conexión y un usuario por defecto (o invitado) en caso de que la persona que use el sistema no posea un usuario asociado. Para este ejemplo, supongamos que un usuario tiene un nombre y un tiempo límite de sesión. Con esta información podemos empezar a armar el esquema del archivo de configuración. El siguiente fragmento XML es el que vamos a usar para guardar la configuración del usuario (aunque tranquilamente podemos darle otro formato):

<Usuario>
    <nombre>[un nombre]</nombre>
    <tiempoSesion>[tiempo en minutos]</tiempoSesion>
</Usuario>

Con esta base podemos empezar el diseño de nuestra aplicación. Antes que nada habría que crear un objeto que represente al usuario. Una vez más, es opcional, pero yo prefiero hacerlo por dos razones, primero porque hace más sencillo el trabajo al encapsular todos los datos de un usuario en un objeto que lo represente, y segundo, porque quiero mostrar como es posible generar objetos a partir de una configuración. Este es el código de la clase Usuario que se me ocurrió a mi:

public class Usuario
{
    public Usuario(){}

    private string _nombre = string.Empty;
    private DateTime _ultimologin = System.DateTime.Now;
    private int _tiempoSesion = 15;

    public string Nombre{
        get{ return this._nombre; }
        set{ this._nombre = value; }
    }

    public DateTime UltimoLogIn{
        get{ return this._ultimologin; }
        set{ this._ultimologin = value; }
    }

    public int TiempoMaximoSesion{
        get{ return this._tiempoSesion; }
        set{ this._tiempoSesion = value; }
    }
}

Como se ve, no tiene ningún misterio. Un usuario tiene un nombre, la fecha de la última vez que ingresó al sistema y el tiempo máximo que puede durar su sesión.

Cómo leer la configuración

A continuación vamos a crear la clase que se va a encargar de "parsear" o serializar la información del archivo de configuración en un objeto de tipo Usuario. Para hacerlo vamos a tener que implementar la interface IConfigurationSectionHandler que se encuentra en el namespace System.Configuration. Esta interface solo posee un método (Create) al que se le pasan 3 objetos:

  • parent: objeto que representa la configuración representada por el nodo padre del nodo con el cual nos encontramos trabajando.
  • configContext: Un objeto de tipo HttpConfigurationContext utilizado solamente cuando trabajamos con paginas ASP.NET.
  • section: XmlNode con el nodo que contiene la configuración con la que vamos a trabajar.
  • En nuestro caso, vamos a crear un UsuarioSectionHandler que lea la configuración del nodo correspondiente al usuario. El esqueleto básico de la clase es asi:

    public class UsuarioSectionHandler : System.Configuration.IConfigurationSectionHandler
    {
        public UsuarioSectionHandler(){}
    
        public object Create(object parent, object configContext, System.Xml.XmlNode section){
                
            // ...
        }
    }

    Lo único que nos interesa ahora es solo el XmlNode section. Ahora, antes de continuar, veamos en donde vamos a usar esta clase.

    Cuando accedemos a la seccion "appSettings" del archivo de configuración usamos la siguiente sentencia:

    string valor = System.Configuration.ConfigurationSettings.AppSettings["parametro configurable"];
    

    A través del objeto ConfigurationSettings accedemos, por su propiedad AppSettings, a cada nodo de la seccion "appSettings". En realidad esto es un atajo, lo que sucede es que se llama a un objeto que implementa la interface IConfigurationSectionHandler, al igual que nuestra clase UsuarioSectionHandler, y este devuelve un NameValueCollection con las configuraciones que pusimos en la seccion "appSettings". Pues nosotros vamos a tener que hacer lo mismo, pero no vamos a tener ningún atajo, por lo que tendremos que acceder a la configuración por medio del método GetConfig() del objeto ConfigurationSettings. Su uso es muy sencillo:

    object valor = System.Configuration.ConfigurationSettings.GetConfig("nombre de la seccion");
    

    donde nombre de la seccion es el nombre de la sección a la que queremos acceder. ¿Cómo sabe nuestra aplicación que objeto tiene que usar para parsear la configuración? Esto se lo indicamos también por medio del archivo de configuración. Para hacerlo tenemos que crear una entrada en la sección llamada "configSections" (por defecto no viene incluida en el archivo) del siguiente modo:

    <configSections>
        <section name="nombre de sección" type="handler de la sección" />
    </configSections>
    

    donde nombre de sección es el nodo o sección con la que deseamos trabajar y handler de la sección es la clase con la cual vamos a parsear la sección, y por lo tanto implementa la interface IConfigurationSecionHandler. Este último atributo tiene una sintaxis especial. Esta consiste de dos partes. La primera es el nombre completo (incluyendo namespace) de la clase, la segunda es la definición del assembly donde se encuentra definida esta clase, separados ambos datos por una coma. La definición del assembly puede consistir solo del nombre de esta (sin extension) o puede incluir tambien la version, cultura, public token key para hacer referencia a un assembly en particular y no a cualquiera. En nuestro caso bastará con colocar lo siguiente en el archivo de configuración:

    <configSections>
        <section name="Usuario" type="PruebaConfiguracion.UsuarioSectionHandler, PruebaConfiguracion" />
    </configSections>
    

    (Tengan en cuenta que PruebaConfiguracion es el nombre del proyecto y namespace donde se encuentra la clase UsuarioSectionHandler)

    Para más información acerca del tema pueden ir aca.

    Ahora con el archivo de configuración completo solo nos resta escribir el contenido del método Create(). Si prestan atención a la definición del método podrán ver que lo que devuelve es un objeto. Esto nos permite devolver lo que queramos, no tiene porque ser un NameValueCollection. En nuestro caso, vamos a devolver un objeto de tipo Usuario. Este es el código del método Create() de UsuarioSectionHandler:

    public object Create(object parent, object configContext, System.Xml.XmlNode section){
        // Si no tenemos el dato obligatorio lanzamos una excepción
        if(section.SelectSingleNode("nombre") == null){
            throw new System.Configuration.ConfigurationException("El nodo 'Usuario' está mal formado!", section);
        }
        
        // Configuramos al usuario
        Usuario u = new Usuario();
        u.Nombre = section.SelectSingleNode("nombre").InnerText;
        u.UltimoLogIn = DateTime.Now;
        
        // Si no encontramos el tiempo, seteamos un valor por defecto
        if(section.SelectSingleNode("tiempoSesion") == null){
            u.TiempoMaximoSesion = 5;
        }else{
            u.TiempoMaximoSesion = Int16.Parse(section.SelectSingleNode("tiempoSesion").InnerText);
        }
        
        return  u;
    }

    Listo, ya esta todo terminado. Ahora con solo ejecutar la siguiente línea vamos a obtener el objeto Usuario del archivo de configuración:

    Usuario usu = (Usuario) System.Configuration.ConfigurationSettings.GetConfig("Usuario");
    

    Código de ejemplo

    El código de ejemplo que acompaña a este artículo contiene todo lo visto aca más otra clase para trabajar con el string de conexión a la base de datos.

     


    Espacios de nombres usados en el código de este artículo:

    System.Configuration
    System.Xml


    Fichero con el código de ejemplo: qrox_configuracion.zip - 8 KB


    ir al índice