En el desarrollo de una arquitectura de n-capas el termino de Entidad de negocio representa el estado de la información que pasará entre cada capa como un conjunto de datos que identifican una entidad como lo es un cliente, una factura, la nomina, la nomenclatura contable, un deposito, etc.
La forma de implementar estas entidades puede variar desde un DataSet hasta un archivo XML que define la estructura de la entidad, en mi experiencia las clases personalizadas para el negocio que no incluyen métodos en su definición sino solamente propiedades que guardan el estado de los datos es una práctica siemple y efectiva.
En este orden de ideas quisiera acuñar este concepto en las mentes de los desarrolladores... Que sucedería si tuviéramos estas clases llamadas Entidades de Negocio pero además de las propiedades que lo definen como lo es un código, un nombre, la dirección, el número del documento o una fecha también pudiéramos definir en cada propiedad el mapeo correspondiente a la columna y al parámetro que requieren para persistir o filtrar la información del proveedor de los datos. Y como si esto fuera suficiente pudiéramos definir la cadena de conexión necesaria para conectarnos al proveedor de datos y definir el nombre de los procedimientos almacenados de los métodos para crear, actualizar, eliminar y listar la información que persiste en el proveedor de datos. Si todo esto es posible gracias a la definición de los atributos dentro del .NET Framework que están disponibles desde la versión 1.1 y gracias a la versión 2.0 podemos utilizar algo conocido como "Generics" para definir tipos Genéricos como clases. A través de un "Parser" y un "Engine" podemos definir la capa de Acceso a Datos única para procesar cualquier entidad del negocio hacia el proveedor de los datos.
El código en C# 2.0 luce de la siguiente manera; Para las entidades de negocio así:
[ Serializable,ConnectionStringName("dbJornalizacion"),CreateMethodName( "TRNTipoTransaccionCrear"),ReadMethodName("TRNTipoTransaccionListar"),UpdateMethodName ("TRNTipoTransaccionActualizar"),DeleteMethodName("TRNTipoTransaccionEliminar")]
public class TipoTransaccion
{
public TipoTransaccion() { }
public TipoTransaccion(int codigo)
{
this.Codigo = codigo;
}
private int? _codigo;
[ColumnName("CodigoTRNTipoTransaccion"), ParameterName("@CodigoTRNTipoTransaccion" ,EsLlavePrimaria=true,IncluirCrear=false)]
public int? Codigo
{
get { return _codigo; }
set { _codigo = value; }
}
private string _nombre;
[ColumnName("Nombre"), ParameterName( "@Nombre")]
public string Nombre
{
get { return _nombre; }
set { _nombre = value; }
}
}
Para la invocación de la capa de Datos como no existe desde la capa lógica de negocio luce así:
public static class TipoTransaccionBLL
{
public static int? Crear(TipoTransaccion parametro)
{
return EngineDAL<TipoTransaccion>.Crear(parametro);
}
public static List<TipoTransaccion> Listar( TipoTransaccion parametro)
{
return EngineDAL<TipoTransaccion>.Listar(parametro);
}
public static bool Actualizar(TipoTransaccion parametro)
{
return EngineDAL<TipoTransaccion>.Actualizar(parametro);
}
public static bool Eliminar(TipoTransaccion parametro)
{
return EngineDAL<TipoTransaccion>.Eliminar(parametro);
}
}
El "Engine" de la Capa de Acceso a Datos luce así, y aunque sea redundar esta "Engine" funciona para cualquier entidad de negocio definida como lo vimos en el primer código. Eso si respetando el estándar de implementación que se definió en la clase EngineDAL, que es para los métodos de crear siempre devuelve un valor entero que es le codigo o el numero, para las operaciones de actualizar y eliminar un valor binario si fue exitosa o no la operación y para le método listar devuelve un colección de entidades o un DataSet.
public class EngineDAL<E> where E:class, new()
{
public static int? Crear(E parametro)
{
int? Id = null;
string connectionStringName = Parser.ObtenerValorConnectionStringAttribute<E>();
string nombreSP = Parser.ObtenerValorMethodNameAttribute<E,CreateMethodNameAttribute >();
Database db = DatabaseFactory.CreateDatabase(connectionStringName);
DbCommand cmd = db.GetStoredProcCommand(nombreSP);
Parser .ParametrosSoloIncluirCrear<E, ParameterNameAttribute>(ref db, ref cmd, parametro);
object codigo = db.ExecuteScalar(cmd);
if (codigo != null)
{
int valor = 0; int.TryParse(codigo.ToString(), out valor); Id = valor;
}
return Id;
}
public static bool Actualizar(E parametro)
{
bool OperacionExitosa = false;
string connectionStringName = Parser.ObtenerValorConnectionStringAttribute<E>();
string nombreSP = Parser.ObtenerValorMethodNameAttribute<E,UpdateMethodNameAttribute >();
Database db = DatabaseFactory.CreateDatabase(connectionStringName);
DbCommand cmd = db.GetStoredProcCommand(nombreSP);
Parser .ParametrosSoloActualizable<E, ParameterNameAttribute>(ref db, ref cmd, parametro);
OperacionExitosa = db.ExecuteNonQuery(cmd)> 0;
return OperacionExitosa;
}
public static bool Eliminar(E parametro)
{
bool OperacionExitosa = false;
string connectionStringName = Parser.ObtenerValorConnectionStringAttribute< E>();
string nombreSP = Parser.ObtenerValorMethodNameAttribute<E,DeleteMethodNameAttribute >();
Database db = DatabaseFactory.CreateDatabase(connectionStringName);
DbCommand cmd = db.GetStoredProcCommand(nombreSP);
Parser .ParametrosSoloLlavePrimaria<E, ParameterNameAttribute>(ref db, ref cmd, parametro);
OperacionExitosa = db.ExecuteNonQuery(cmd) > 0;
return OperacionExitosa;
}
public static List<E> Listar(E parametro)
{
List<E> Listado = null;
string connectionStringName = Parser.ObtenerValorConnectionStringAttribute<E>();
string nombreSP = Parser.ObtenerValorMethodNameAttribute<E,ReadMethodNameAttribute >();
Database db = DatabaseFactory.CreateDatabase(connectionStringName);
DbCommand cmd = db.GetStoredProcCommand(nombreSP);
Parser .ParametrosSoloFiltro<E>(ref db, ref cmd, parametro);
IDataReader idr = null;
try
{
idr = db.ExecuteReader(cmd);
Listado = Parser.Query<E>(idr);
}
finally
{
if (idr != null && !idr.IsClosed) idr.Dispose();
}
return Listado;
}
public static DataSet ListarDS(E parametro)
{
DataSet DS = null;
string connectionStringName = Parser.ObtenerValorConnectionStringAttribute<E>();
string nombreSP = Parser.ObtenerValorMethodNameAttribute<E, ReadMethodNameAttribute >();
Database db = DatabaseFactory.CreateDatabase(connectionStringName);
DbCommand cmd = db.GetStoredProcCommand(nombreSP);
Parser .ParametrosSoloFiltro<E>(ref db, ref cmd, parametro);
DS = db.ExecuteDataSet(cmd);
return DS;
}
public static bool Existe(E parametro)
{
bool Existe = false;
string connectionStringName = Parser.ObtenerValorConnectionStringAttribute<E>();
string nombreSP = Parser.ObtenerValorMethodNameAttribute<E,ExistsMethodNameAttribute >();
Database db = DatabaseFactory.CreateDatabase(connectionStringName);
DbCommand cmd = db.GetStoredProcCommand(nombreSP);
Parser .ParametrosSoloLlavePrimaria<E, ParameterNameAttribute>(ref db, ref cmd, parametro);
object valor = db.ExecuteScalar(cmd);
if (valor != null) Existe = (bool)valor;
return Existe;
}
}
Por cuestiones de abreviar el ejemplo y mostrar la optimización de la capa de acceso a datos en base a las "entidades de negocio inteligentes", se eliminaron las líneas extras, validaciones y procesos que pudieran existir en la capa de negocios.
Hasta el próximo articulo nos vemos!.
Manolo Herrera