Arquitectura de Software

Utilizando .net Remoting, Programación en Capas, Programación Orientada a Objetos y patrones de programación, (SqlServer 2000 Back End)

Parte II: Manejo de Entidades y Colecciones 


 

Fecha: 06/Ago/2005 (5 de Agosto de 2005)
Autor: Rodolfo Hernandez Pulido. e-mail:[email protected]

 


Esta Capa se utiliza para la comunicación o medio de transporte entre las demás capas para llevar y traer datos, regularmente a través de parámetros.
Estas clases dependen de clases bases con ciertas funcionalidades de control interno, como por ejemplo saber el estatus de la entidad, si se a modificado o no, si es nueva, si se quiere eliminar, etc. y todas ellas son seriabilizables para poderlas utilizar como parámetros a través de .net remoting. En resumen, nos sirven de mapeo de las tablas físicas del SqlServer hacia la memoria.

Maneja dos clases bases, una para Entidades, y otra Para Colecciones.

     La clase IEntityBase, debido a que es abstracta, nos obliga a heredarla para cada entidad que vayamos a utilizar, así compartimos toda la funcionalidad de ella, y también, si hacemos un cambio en esta, automáticamente se refleja en todas las entidades. 

     Dentro de las funcionalidades de esta clase podemos ver la propiedad UltimaActualizacion , la cual nos sirve para controlar la concurrencia de la información, ya que recordemos que cada entidad es el mapeo de un registro de la tabla física del sql.
¿Como funciona?
Supongamos este escenario: Se tiene funcionando el Servicio de Windows de .Net Remoting el cual expone métodos publicos para acceso a datos, suponiendo que uno de ellos es  [public Entity.Pais TraerPaises(int pais)],  el cual regresa una clase de Tipo Entity.Pais la cual hereda de IEntityBase y recibe un parametro de tipo entero. Siguiendo con las suposiciones, un usuario en la sucursal "X" corre ese metodo y obtiene a memoria una instancia del pais "1", el cual tiene la descripcion "A" y le cambia la descripcion a "B", al mismo tiempo otro usuario en la sucursal "Y", obtiene a memoria también la entidad del pais "1", y coincidentemente, le cambia la descripcion a "C" e inmediatamente lo manda almacenar exitosamente. ¿Que pasa cuando el primer usuario almacene el pais 1?

Con el control de la ultima actualizacion nos protegemos de esos escenarios, por que cuando el usuario 1 llamo la entidad Pais a memoria, obtubo de la tabla de SQL el timestamp de la version del registro en la propiedad UltimaActualizacion,  cuando el usuario 2 actualiza los datos, automaticamente se modifica la version del registro, asi cuando el usuario 1 pretende actualizar los datos no grabara, debido a que esa entidad que el tiene ya no es la que esta en la tabla de la base de datos, y eso lo controlamos a traves de la UltimaActualizacion. 

     Otra funcionalidad que se puede ver dentro de la clase base para entidades es EntityEstatus. La cual nos sirve para saber, antes de guardar la informacion, si la entidad tiene cambios o no, o si es nueva, para tomar la desicion de como manejamos la persistencia  de ella. Esas validaciones se ven aplicadas en la Capa de Acceso a Datos (DAL).

A continuacion el codigo de la clase EntityBase.cs:

using System ;
using System.Xml.Serialization;
using System.ComponentModel;
using System.Collections;
namespace Entity
{
/// <summary>
/// clase base para todas las entidades para manejar los estatus
/// </summary>
[Serializable]
public abstract class IEntityBase
{
#region Atributos
private int ultimaActualizacion;
protected Entity.Estatus estatusEntidad;
#endregion


#region Constructor Destructor
/// <summary>
/// Constructor
/// </summary>
protected IEntityBase()
{
this.estatusEntidad = Entity.Estatus.Agregado;
}
#endregion


#region Propiedades
/// <summary>
/// Obtiene o establece un numero hash determinando la ultima vez que se modifico
/// </summary>
public int UltimaActualizacion
{
get{return this.ultimaActualizacion;}
set{this.ultimaActualizacion=value;}
}


/// <summary>
/// Obtiene o Establece un Entity.Estatus para un IEntityBase
/// </summary>
public Entity.Estatus EntityStatus
{
get{return this.estatusEntidad;}
set{this.estatusEntidad = value;}
}


#endregion

#region Métodos Públicos
/// <summary>
/// Acepta los cambios realizados desde la ultima vez que se llamo AcceptChanges
/// </summary>
public void AcceptChanges()
{
this.estatusEntidad = Entity.Estatus.SinCambios ;
}
/// <summary>
/// Determina si la entidad tiene cambios
/// </summary>
/// <returns>bool</returns>
public bool HasChanges()
{
return (this.estatusEntidad == Entity.Estatus.Agregado || this.estatusEntidad == Entity.Estatus.Modificado);
}


/// <summary>
/// Actualiza el estatus de la entidad a modificado
/// </summary>
public void Set()
{
if (this.estatusEntidad == Entity.Estatus.SinCambios)
{
this.estatusEntidad = Entity.Estatus.Modificado;
}
}
/// <summary>
/// Serializa la entidad a un string en formato Xml
/// </summary>
/// <returns><see cref="string"/></returns>
public string ToXml()
{
System.Text.StringBuilder stringBuilder=null;
try
{
System.Xml.Serialization.XmlSerializer serializador = new System.Xml.Serialization.XmlSerializer(this.GetType(),new XmlRootAttribute(this.GetType().ToString()));

stringBuilder = new System.Text.StringBuilder();
System.IO.StringWriter stringWriter = new System.IO.StringWriter(stringBuilder);
serializador.Serialize(stringWriter, this); 
}
catch(Exception ex)
{
Entity.LogController.Instance().AgregarMensaje(ex.ToString() ,true);
}
return stringBuilder.ToString();
}


#endregion


/// <summary>
/// Marca como borrados al detalle del maestro
/// </summary>
/// <param name="listaElementos">ICollectionBase del detalle a marcar</param>
public void BorrarDetalle(ICollectionBase listaElementos)
{
ArrayList listaBorrados = new ArrayList(listaElementos);
foreach (IEntityBase elemento in listaBorrados)
{
listaElementos.Remove(elemento);
}
}
}
}



     Por otro lado tambien tenemos la clase base de las colecciones, que representarian toda una tabla en memoria. Nos permite hacer manejos de informacion masivos. Aqui podemos ver los metodos de toda coleccion, como agregar, eliminar, contador de elementos, etc...

     En el caso del método public virtual void Remove(IEntityBase entityBase) , llevamos un control de los elementos que se han eliminado de esta lista en otra lista del mismo tipo, para que posteriormente en la Capa de Acceso a Datos (DAL) se borren fisicamente de la base de datos. Esta coleccion de elementos borrados se obtiene a traves del metodo public ICollectionBase GetDeleted()  . 

using System;
using System.Xml; using System.Xml.Serialization;
namespace Entity
{
/// <summary>
/// clase base de colecciones para todas las colecciones
/// </summary>
///
[Serializable]
public abstract class ICollectionBase : System.Collections.CollectionBase
{
ICollectionBase listaBorrados;

protected ICollectionBase()
{
//listaBorrados = (ICollectionBase)Activator.CreateInstance(this.GetType());
}

/// <summary>
/// Agrega un elemento IEntityBase a la colección
/// </summary>
/// <param name="entityBase">cualquier clase heredada del IEntityBase</param>
/// <returns>int</returns>
public int Add(IEntityBase entityBase)
{
return List.Add(entityBase);
}

/// <summary>
/// Quita el objeto IEntityBase con el objeto especificado
/// </summary>
/// <param name="entityBase">objeto IEntityBase a quitar</param>
public virtual void Remove(IEntityBase entityBase)
{
if (listaBorrados == null)
{
listaBorrados = (ICollectionBase)Activator.CreateInstance(this.GetType());
}
entityBase.EntityStatus = Entity.Estatus.Borrado;
this.listaBorrados.Add(entityBase);
List.Remove(entityBase);
}

/// <summary>
/// Quita el objeto IEntityBase con el objeto especificado
/// </summary>
/// <param name="entityBase">objeto IEntityBase a quitar</param>
public void RemoveElementOfList(IEntityBase entityBase)
{
List.Remove(entityBase);
}

/// <summary>
/// Inserta un objeto IEntityBase en la posicion especificada
/// </summary>
/// <param name="index">pocicion de la colección donde se va a insertar el objeto</param>
/// <param name="entityBase">cualquier clase heredada del IEntityBase</param>
public void Insert(int index, IEntityBase entityBase)
{
List.Insert(index, entityBase);
}

/// <summary>
/// Determina si la colección contiene una clase IEntityBase
/// </summary>
/// <param name="entityBase">cualquier clase heredada del IEntityBase</param>
/// <returns>bool</returns>
public bool Contains(IEntityBase entityBase)
{
return List.Contains(entityBase);
}

/// <summary>
/// Obtiene el objeto listaBorrados de la colección
/// </summary>
/// <returns>IColectionBase</returns>
public ICollectionBase GetDeleted()
{
if (listaBorrados == null)
{
listaBorrados = (ICollectionBase)Activator.CreateInstance(this.GetType());
}
return listaBorrados;
}
/// <summary>
/// Confirma todos los cambios realizados en la colección desde la ultima vez que se llamo AcceptChanges
/// </summary>
public void AcceptChanges()
{
foreach (Entity.IEntityBase elemento in this.List)
{
elemento.AcceptChanges();
}
if (this.listaBorrados != null)
{
this.listaBorrados.Clear();
}
}

/// <summary>
/// Obtiene un valor que indica si hay elementos eliminados dentro de la colección
/// </summary>
/// <returns>Regresa verdadero si existen elementos eliminados o de lo contrario falso</returns>
public bool HasDeleted()
{
return (this.listaBorrados != null && this.listaBorrados.Count > 0);
}

/// <summary>
/// Convierte la clase a un XML
/// </summary>
/// <returns>Cadena XML que representa a la clase</returns>
public string ToXml()
{
System.Text.StringBuilder stringBuilder=null;
try
{

System.Xml.Serialization.XmlSerializer serializador = new System.Xml.Serialization.XmlSerializer(this.GetType(),new XmlRootAttribute(this.GetType().ToString()));
stringBuilder = new System.Text.StringBuilder();
System.IO.StringWriter stringWriter = new System.IO.StringWriter(stringBuilder);
serializador.Serialize(stringWriter, this);

}
catch(Exception ex)
{
Entity.LogController.Instance().AgregarMensaje(ex.ToString() ,true);
}
return stringBuilder.ToString();
}


/// <summary>
/// Realiza una copia de la colleccion hacia un arreglo
/// </summary>
/// <param name="array">IEntityBase arreglo donde se dejara la informacion</param>
/// <param name="index">indice a partir de donde se va a copiar</param>
public void CopyTo(IEntityBase[] array, int index)
{
this.InnerList.CopyTo(array,index);
}

/// <summary>
/// Obtiene el indice del elemento especificado
/// </summary>
/// <param name="entidad">entidad a buscar</param>
/// <returns>int</returns>
public int IndexOf(IEntityBase entidad)
{
return this.InnerList.IndexOf(entidad);
}
}
}


Ir a parte III: Capa de Acceso a Datos (DAL)

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

using System;
using System.Xml;
using System.Xml.Serialization;
using System.Collections;


 


Fichero con el código de ejemplo: rhernandez_Entity.zip - Tamaño 16 KB


Arquitectura de Software utilizando Net Remoting

ir al índice