Maldivas Arquitectura 2 – Entidades y Reflexión

Como he contestado en algún Post, Maldivas es un Framework que propone una solución concreta para un momento determinado, en que la tecnología de Microsoft solo permitía utilizar su modelo de datos basado casi exclusivamente en Dataset o tenias que utilizar frameworks de terceros como n-hibernate, nace de una necesidad en concreto para dar solución a un proyecto que comenzó al finales del año 2005, en ningún caso propongo este Framework para utilizar en otras aplicaciones. Este modelo está basado por completo en el Entity Relationship Model, que existe hace mas de 30 años y al que hemos dotado de funcionalidad adicional para minimizar el código requerido y dotarle de otras características.

Si tuviera que desarrollar de nuevo este proyecto, seguramente, utilizaría el Entity Framework, que se basa en un modelo de entidades similar al que presento aquí. En cualquier caso el proceso de reflexión se utiliza en gran parte del framework de .net y os puede servir para aplicarlo en algunos procesos de vuestros desarrollos, un ejemplo claro lo podéis ver cuando desarrolláis sobre Web Services en que la información de los contratos, serialización y otros se realiza en forma de atributos.

El primer proceso es el de representar en nuestro lenguaje, en este caso c#, las entidades correspondientes a cada una de las tablas, vistas, funciones y otros objetos de la base de datos con los que nos queramos interactuar, para que podamos trabajar con ellas desde nuestro lenguaje. En este caso cada clase que denominaremos “Entidad” es la representación de una tabla de nuestra base de datos. Partiremos de una entidad sencilla que almacena la información de una tabla de la base de datos, esta entidad compuesta de cuatro campos, está decorada con varios atributos que definen varios aspectos de la tabla a la que hace referencia. Es lo que habitualmente denominamos Metadatos. Como se observa en este ejemplo, la entidad es una clase en c# que representa una de las tablas de la base de datos.

using System;

using Maldivas.Entities.Attributes;

namespace Maldivas.Entities.Gestion

{

[Serializable]

[EntityStructure(DataBase = “Maldivas_oran”, Schema = “Gestion”, Name = “Articulos_embalajes_tipos”, EntityType = “Table”)]

public class Articulos_embalajes_tipos : Entity, IEntities<Articulos_embalajes_tipos>

{

#region Fields enum

 public enum Fields

{

Codaux, Codigo, Descripcion, Observaciones,

}

#endregion

[EntityAttributes(FieldName = “Codaux”, IdentityValue = 1, IdentityStep = 1, Lenght = 10, IsReadOnly = true)]

public int Codaux { get; set; }

[EntityAttributes(FieldName = “Codigo”, PrimaryKey = 1, Lenght = 2)]

public string Codigo { get; set; }

[EntityAttributes(FieldName = “Descripcion”, AllowNull = true, Lenght = 50)]

public string Descripcion { get; set; }

[EntityAttributes(FieldName = “Observaciones”, AllowNull = true, Lenght = 8000)]

public string Observaciones { get; set; }

#region IEntities<Articulos_embalajes_tipos> Members

public virtual Articulos_embalajes_tipos Clone()

{

Articulos_embalajes_tipos articulos_embalajes_tipos = new Articulos_embalajes_tipos();

articulos_embalajes_tipos.Codaux = Codaux;

articulos_embalajes_tipos.Codigo = Codigo;

articulos_embalajes_tipos.Descripcion = Descripcion;

articulos_embalajes_tipos.Observaciones = Observaciones;

return articulos_embalajes_tipos;

}

 public virtual void Copy(Articulos_embalajes_tipos ent)

{

Codaux = ent.Codaux;

Codigo = ent.Codigo;

Descripcion = ent.Descripcion;

Observaciones = ent.Observaciones;

}

 #endregion

 public new static string ToString()

{

return “Articulos_embalajes_tipos”;

}

}

}

Existen dos tipos de atributos en la clase, el primero que decora la clase:

[EntityStructure(DataBase = “Maldivas_oran”, Schema = “Gestion”, Name = “Articulos_embalajes_tipos”, EntityType = “Table”)]

Este atributo nos indica algunas propiedades de la base de datos como el Nombre de la base de datos el esquema, el nombre y el tipo de entidad” Tabla, Vista, Función, etc.

Y el segundo que decora cada columna de la tabla con un atributo similar a este:

[EntityAttributes(FieldName = “Codigo”, PrimaryKey = 1, Lenght = 2)]

En el se indican las propiedades de cada columna, estas serán utilizadas a lo largo de toda la aplicación y nos permitirán obtener la información relativa a la configuración de cada campo en la tabla.

Hemos incluido dos métodos, el primero “Clone” nos permite realizar una copia de cualquier entidad en un nuevo objeto y el segundo “Copy”, nos permite copiar la información de otra entidad en la actual, estos métodos son utilizados por diferentes secciones a lo largo del programa. Pensamos también incorporar el patrón Observer, pero al final lo descartamos ya que en principio, no nos iba a hacer falta.

Una de las primeras preguntas que nos hacemos es: ¿cómo podemos acceder a esta información que almacenamos en forma de atributos en una clase?, la respuesta seria utilizando reflexión. Reflexión es una tecnología que nos permitirá preguntar al ensamblado de una clase información acerca de esta, básicamente lo que hace es abrir el archivo del ensamblado y buscar la información del atributo con el que hemos decorado la clase. Para entender el proceso os voy a poner un ejemplo sencillo, uno que nos permita únicamente almacenar el nombre de la base de datos de una clase. El primer paso sería el de crear una clase que deriva de Attribute y nos permita almacenar la información que queramos. Para más información podeís ver en http://msdn.microsoft.com/es-es/library/z0w1kczw(VS.80).aspx

/// Creación del atributo

using System;

namespace Maldivas.Entities.Attributes

{

[Serializable, AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]

public sealed class EntityStructure : Attribute

{

/// <summary>

/// Permite configurar el nombre de la Base de datos

/// </summary>

/// <param name=”database”>Nombre de la Base de datos</param>

public EntityStructure(string databaseName)

{

DBName = databaseName;

}

/// <summary>

/// Propiedad que permite acceder y configurar al nombre de la tabla

/// </summary>

public string DBName { get; set; }

}

}

 

/// Creación de la clase que lo utiliza

[EntityStructure(“AdventureWorks”)]

public class Ejemplo

{

}

/// Creación de un metodo que nos permite leer el atributo de la clase

public class ReadDataBaseAttribute()

{

string DataBaseName = null;

Type type = typeof(Ejemplo);    

/// Aqui se realiza el proceso de reflexión

object[] oestructure = type.GetCustomAttributes(typeof(EntityStructure), false);

/// Comprueba que haya leido algún valor

if (oestructure != null && && oestructure.Length > 0)

{

EntityStructure es = oestructure[0] as EntityStructure;

if (es != null)

DataBaseName = es. DBName;

}

return DataBaseName;

}

De esta forma la variable DataBaseName obtendría su valor AdventureWorks.

Debido a que el proceso de reflexión tiene un alto coste, ya que hay que abrir el ensamblado y buscar la información, puede ser aconsejable cachear la información si la vamos a utilizar muchas veces, nosotros tenemos un proceso, en el que, cuando se solicita información de una entidad la primera vez, esta se almacena en cache, de esta forma minimizamos el coste del proceso.

Escribir una clase similar a la del primer ejemplo por cada una de las tablas que tengamos, puede ser un proceso muy costoso, para automatizar este proceso, desarrollamos un pequeño programa que automatiza la creación de los ficheros de entidades utilizando Codedom, del que intentare escribir un post en entradas posteriores, podéis ver un pequeño ejemplo aquí http://www.15seconds.com/issue/020917.htm , aunque existen otras formas de hacerlo, esta es la más adecuada, ya que nos permitirá generar código en diferentes lenguajes. El aspecto de nuestra herramienta es el siguiente:

De igual forma Entity Framework dispone de una herramienta para realizar este proceso de forma automática.

A petición de Eduardo Obregon, mi compañero de trabajo, que me preguntaba sobre cuánto es de costosa la reflexión y la ganancia que se obtiene cacheando datos, he adjuntado un archivo de test para realizar esta comprobación, en el equipo donde lo he probado, mis resultados son los siguientes. Para leer 10 millones de atributos de una entidad, el equivalente a 100000 tablas con unos 100 campos por tabla, el equipo ha tomado un tiempo aproximado de 40 segundos, cacheando se ha reducido a los 3 segundos, ya que el atributo solo ha sido leído una vez, en realidad el tiempo de cacheo será algo mayor ya que la inicialmente la lectura de los atributos a cachear siempre se tiene que realizar la primera vez. Despues del test creo que el cacheo no tiene mucho sentido. Cada uno que saque sus propias conclusiones.

Os dejo el fichero que contiene el ejemplo Test Atributos y Reflexión

 

2 comentarios en “Maldivas Arquitectura 2 – Entidades y Reflexión”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *