Un Momento de Reflexión (Reflection) en la Vida Diaria

Esta vez un problema que se me presento me obligó a reflexionar, reflexionar y reflexionar…, el caso es el siguiente:

Tengo una clase que representa a una entidad, mas específicamente hablando la clase es la que se genera utilizando la plantilla NetTiers para CodeSmith, que se corresponde uno a uno con las tablas de la base de datos y que expone mediante sus propiedades las columnas de la tabla correspondiente, lo que necesito es cargar automáticamente las propiedades de la clase a sus correspondientes controles (TextBox) de un WinForm.

Necesito crear un método general que funcione con cualquier tipo de entidad, y como no utilizaré DataBinding, lo que se me ocurrió es utilizar la propiedad Tag que tienen todos los controles para poner ahí el nombre de la propiedad de la clase entidad que le corresponde.

El siguiente paso es recorrer las propiedades del objeto entidad e ir copiando los valores en el componente que le corresponde, pero ¿cómo recorrer las propiedades de una clase?; esto fue lo que me llevo a la Reflexión.

Reflexión.- “La reflexión es uno de los pilares de .NET. Esta característica permite almacenar y obtener información en tiempo de ejecución sobre casi cualquier objeto o tipo presente en un módulo. Es gracias a esto que es posible implementar técnicas fundamentales como la recolección de basura o la serialización en distintos formatos. Y aunque es cierto que la mayoría de los entornos de programación modernos proporcionan algún tipo de RTTI (runtime type information, el pariente pobre de la reflexión), nunca antes se había visto un uso tan extenso y generalizado de este recurso como en .NET.”

La definición anterior la tomé prestada de la página de uno de mis maestros Ian Marteens, les recomiendo que consulten un artículo muy ilustrativo con ejemplo incluido sobre reflexión en el siguiente link: Calling Dr. Marteens: Reflexión.

Utilizando Reflexión podemos recorrer con facilidad las propiedades de una clase:

public void CargarDatosAControles(Object entidad, Type tipo,
    Control contenedor)
{
    foreach (PropertyInfo info in tipo.GetProperties())
    {
        Attribute atributo = Attribute.GetCustomAttribute(info,
            typeof(DataObjectFieldAttribute));
        if (atributo != null)
        {
            foreach (Control control in contenedor.Controls)
            {
                 if (control.Tag != null &&
                    control.Tag.ToString() == info.Name)
                {
                    control.Text = info.GetValue(entidad,
                        null).ToString();
                }//if
            }//foreach
        }//if
    }//foreach
}

El método que se presenta en el ejemplo, utiliza tres parámetros, el primero es una referencia a un objeto entidad, el segundo es el tipo de la clase entidad y el tercer parámetro es la referencia a un control que contiene a los controles (TextBox) donde queremos cargar los datos de la clase entidad, por ejemplo un Panel.

Para comenzar podemos obtener información de las propiedades de un tipo utilizando el método GetProperties() que nos devuelve una colección de objetos PropertyInfo que contiene la información de todas las propiedades de un tipo en este caso una clase entidad.

Un problema que se me presento es que cuando recorres las propiedades de un tipo, el método GetProperties() devuelve todos las propiedades, y resulta que la clase entidad con la que estoy trabajando aparte de las propiedades que se corresponden con las columnas de la tabla de base de datos que representa, tiene mas propiedades.

Entones tengo que discriminar cuales son las propiedades que corresponden a una columna de una tabla de base de datos y cuales no. Para hacer eso tengo que utilizar la información que proporcionan los atributos que se asocian a la declaración de las propiedades en el código fuente. Revisando el código de la clase entidad encuentro que las propiedades que me interesan tienen asociado a ellas un atributo llamado DataObjectFieldAttribute que indica que la propiedad es un campo de datos, la definición de la entidad luce así:

[DataObjectField(false, false, false, 300)]
public virtual System.String RazonSocial

Lo cual indica que la propiedad RazonSocial es un campo de datos, por los 4 parámetros del constructor del atributo podemos deducir que: no es clave primaria, no es un campo de identidad es decir autogenerado, no acepta valores nulos, y tiene una longitud de 300.

Ahora regresemos a nuestro método CargarDatosAControles, en el cual tenemos la siguiente instrucción:

Attribute atributo = Attribute.GetCustomAttribute(info,
    typeof(DataObjectFieldAttribute));

El código anterior obtiene un atributo de tipo DataObjectFieldAttribute a partir del objeto PropertyInfo de una propiedad.

Luego verificamos que el atributo obtenido sea diferente de null lo cual indica que la propiedad posee ese atributo y por tanto es un campo de datos, solo así procedemos a recorrer los controles del contendedor y buscar a que componente (TextBox) le corresponde la propiedad que estamos tratando, obtenemos el valor de la propiedad a través del método GetValue(entidad, null) de la clase PropertyInfo, para lo cual necesitamos como primer parámetro una instancia de la clase entidad y como segundo parámetro le pasamos null porque no estamos tratando con propiedades indexadas.

La manera de llamar al método seria la siguiente:

CargarDatosAControles(cliente, typeof(Cliente), panelControl1);

De manera similar se pude implementar el otro método que hace la operación inversa CargarDatosAClases.

No comments yet

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: