martes, 23 de enero de 2007

“Entidades de Negocio Inteligentes”

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

viernes, 19 de enero de 2007

FW: Próxima reunión de la Comunidad de Desarrolladores.






Tema: LINQ

Expositor: Marlon Ramos (Miembro del Buró de Expositores de la comunidad .NET Community ) Además posee las siguientes certificaciones: MCSA, MCDBA, MCAD .Net, MCT, MCTS

Fecha: miércoles 24 de Enero 2007

Horario: 6:30 P.M. – 8:30 P.M.

Lugar: Oficinas Microsoft. Edificio Intercontinental Zona 10, 11 Nivel.

Costo: Gratis

Otros: Habrá Coffee Break, Parqueo y Premios

Invita: Comunidad de Desarrolladores .NET en Guatemala, con Apoyo de Microsoft de Guatemala

Un Extracto de la definición en el sitio de MSDN sobre el tema que se desarrollará: “The LINQ Project is a codename for a set of extensions to the .NET Framework that encompass language-integrated query, set, and transform operations. It extends C# and Visual Basic with native language syntax for queries and provides class libraries to take advantage of these capabilities.”

Los esperamos!

Manolo Herrera

Coordinador de La Comunidad de Desarrolladores .NET en Guatemala (Blog: http://jmhogua.blogspot.com/)


miércoles, 17 de enero de 2007

Corrección del error al invocar un Servicio Web Remoto al utilizar autenticación por Windows

Este error es muy común cuando emprendemos el viaje a migrar a una arquitectura de servicios. Nos funciona bien localmente, pero cuando queremos acceder el servicio desde otro servidor distinto del que corre la aplicación nos da este error:

The request failed with HTTP status 401: Access Denied.


Esto sucede porque las credenciales con que se autenticaron en la aplicación web no pasan automáticamente al invocar el servicio web en otro servidor o a veces en el mismo servidor. La clave esta en luego de instanciar el servicio Web apunanto a la clase proxy del proyecto:

EmpresaPROXY.EmpresaWSL servicio = new EmpresaPROXY.EmpresaWSL ();


debemos agregar la siguiente linea:

servicio.Credentials = System.Net.
CredentialCache.DefaultCredentials;
Esta instrucción indica que las credenciales de autenticación pasaran a la invocación del servicio Web y con ello si el web.config esta definido como autenticación por windows y no tienen ningún filtro de roles o usuarios o bien que si estan definidos y el usuario pertenece alguno de ellos el servicio devolverá el resultado esperado.
Hasta la proxima amigos!,

Manolo Herrera

jueves, 11 de enero de 2007

Primera Reunión de la comunidad ha sido un exito y un futuro prometedor

Ayer miércoles por la noche tuvimos la primera reunión de la comunidad de desarrolladores .NET en Guatemala en las oficinas de Microsoft. Y tuvimos una asistencia considerable de 26 personas incluyendonos todos.

El Ingeniero y amigo Carlos Lone que recien terminó su maestría MBA (Managment Business Administration) , compartio con nosotros de una manera introductoria pero muy acertada y con "demos" que nos hicieron viajar nuestra imaginación hacia un futuro prometedor que los que nos dedicamos a esto del "desarrollo de software" nos permite dislumbrar dicho futuro sobre las nuevas tecnologiás de Microsoft sobre las plataformas .NET en su nueva version 3.0. El tema Windows Presentation Fundation y los productos Expressions nos dejo a todos con las ganas de seguir aprendiendo y conocimiento de este cambiante y progresivo avance tecnológico en el cual nos encontramos sumergidos.

El interés y compañerismo en medio de la reunión no se hicieron esperar y por ello con buen pie decimos que esta primera reunión del año fue un éxito!.

Con el propósito de guardar un registro de la ya incipiente comunidad de desarrolladores abajo detallo los participantes y amigos que dejaron el registro de su presencia en esta primera reunión del año 2007.


andy_martinez00@hotmail.com
Andres Adan Martinez
aovalle84@gmail.com
Alejando Francisco Ovalle Arrecis
carlone@galileo.edu
Carlos Lone
celsorojas@gmail.com
Celso Estuardo Rojas Bocanegra
Christian_Rodas@hotmail.com
Christian Rene Rodas Ortiz
dante.cifuentes@gmail.com
Dante Cifuentes
dorianhidalgo@hotmail.com
Dorian Alberto Hidalgo Vallejo
fausto.lopez@gmail.com
Fausto Antonio Lopez Mansilla
francisco.gonzales@bayarcropscience.com
Juan Francisco Gonzalez
franzlopez@hotmail.com
Franz Lopez
Jeemsk8@hotmail.com
Jose Emmanuel Espinoza Marroquin
jmveliz@gmail.com
Jorge Veliz
jrivera@cygnum.net
Jorge Ali Rivera Garcia
lcsandoval@gmail.com
Luis Carlos Sandoval
marlon.ramos@gmail.com
Marlon Antonio Ramos Moscoso
mchajon@gmail.com
Mario Estuardo Chajón Arriaza
mgalindo077@gmail.com
Miguel Octavio Galindo Mena
mherrera@launion.com.gt
Manolo Herrera
mherrerau_dps@yahoo.com
Marcel Herrera Urizar
oscar.robles@zaculeuvalley.com
Oscar Cuán Robles
srodriguez142857@gmail.com
Sergio Jose Rodriguez Mendez
webmaster@eDigitalGT.com
Ary Eslizar Rivera Mendez

Hasta pronto y nos vemos luego...

Manolo Herrera
Coordinador de La Comunidad de desarrolladores .NET en Guatemala

Ayer tuve un sueño..

Dicen por mi pueblo Guatemala, que de poetas y locos tenemos todos un poco... Interrumpiendo un poco la parte técnica de este blog para mis lectores aquí les va este pensamiento:

Ayer tuve un sueño...En el cual pensaba sobre la necesidad de un Rompimiento… Del Status Quo, de mis esquemas mentales, de seguir la corriente en la cual todos estamos inmersos.


Para ello se necesita coraje pensaba, y salir del conformismo... No quiero incitar a la rebeldía sino al cambio, al progreso personal, al mejoramiento de nuestras vidas… Pero para llegar lejos debemos de hacer algo diferente a lo que estamos acostumbrados a hacer… Perdón me corrijo mas bien es a pensar porque los pensamientos nos han llevado donde estamos, estas conforme donde estas o esperas algo mejor?, Quiero decirte que ambos pensamientos son lo mismo, te dejan inmóvil.

Haz algo por ti y en el camino no pierdas la oportunidad de bendicir a otros con tu vida.

Te Atreves!.



Autor: Manolo Herrera

martes, 9 de enero de 2007

Como hacer para invocar remotamente los metodos de un web service

Nos sucede que cuando tenemos el web services local podemos probarlo pero si este esta en otro servidor que no es el local es decir remoto, nos indica que esta operación no puede realizarse. Como hacer, pues en las siguientes líneas aprenderemos como:

Es una opción necesaria cuando queremos garantizar que el servicio web que publicamos en realidad funciona. No es aconsejable en servidores de producción sino para servidores de desarrollo ya que esta abierto para cualquier usuario que pueda autenticarse en dicho servidor los servicios WEB. Por ello no es recomendable por asuntos de seguridad pero si para realizar una prueba en el lugar de publicación. Aquí el código que debe de ir en el WEB.CONFIG dentro de la sección <system.web> debe de ir, la sección <webServices> como se muestra a continuación:

<webServices>

<protocols>

<add name="HttpGet" />

<add name="HttpPost"/>

</protocols>

</webServices>

Hasta pronto,

Manolo Herrera

sábado, 6 de enero de 2007

Programación en Capas Primera Parte (Capa de Acceso a Datos)

Esta metodología de trabajo a probado ser muy efectiva durante ya varias décadas. Cada vez mas las herramientas de desarrollo como .NET nos permiten acercarnos mas y mas a los principios de programación OOP. La separación de programación en capas para aplicaciones comerciales permite la reutilización, el polimorfismo, la independencia y cooperación entre objetos o componentes. Pero sucede que cuando iniciamos la implementación de esta metodología nos es difícil aplicar los principios de abstracción y encapsulación. Para ello es este tema que espero desarrollar en este y otros artículos y juntos podamos aprender. Nivel Requerido para lectores: Medio.

El modelo de N-Capas conocido en ingles como N-tiers o Layers define las siguientes capas de Abajo hacia arriba:

Capa de acceso a datos: Capa que sirve entre como puente entre la capa lógica de negocio y el proveedor de datos. Este capa pretende encapsular las especificidades del proveedor de datos tales como (SQL, Oracle, Sybase, archivos XML, texto, hojas electrónicas), a la siguiente capa. Para que si cambia el proveedor de datos solo necesitemos cambiar en una sola capa el proveedor de datos. Hoy en día gracias a la tecnología disponible y a la expansión del conocimiento a través del Internet, tenemos a nuestra deposición la librería de Microsoft Enterprise Library en su versión Dos, donde podemos acceder sin necesidad de cambiar el código a proveedores OLEDB, SQL, Oracle, XML, archivos Excel, etc. Por lo que si programamos en .Net por capas la capa de acceso a datos debemos de utilizar estas librerías para dejarle el trabajo a la misma y nosotros solo preocuparnos con la conexión al proveedor de los datos y nada mas. Un ejemplo de cómo luce esta capa aplicando las librerías de Microsoft es como muestro a continuación esta escrito en C# 2.0:

using System;

using System.Collections.Generic;

using System.Text;

using System.Data;

using Microsoft.Practices.EnterpriseLibrary.Common;

using Microsoft.Practices.EnterpriseLibrary.Data;

using System.Data.Common;

namespace LaUnion.Sistema

{

public static class ConceptoJornalizacionDAL

{

private const string NombreInstancia = "dbJornalizacion";

private const string SPCrear = "TRNConceptoJornalizacionCrear";

private const string SPActualizar = "TRNConceptoJornalizacionActualizar";

private const string SPEliminar = "TRNConceptoJornalizacionEliminar";

private const string SPListar = "TRNConceptoJornalizacionListar";

public static int? Crear(ConceptoJornalizacion parametro)

{

int? Resultado = null;

Database db = DatabaseFactory.CreateDatabase(NombreInstancia);

DbCommand cmd = db.GetStoredProcCommand(SPCrear);

Parser.ParametrosSoloIncluirCrear<ConceptoJornalizacion>(ref db, ref cmd, parametro);

object codigo = db.ExecuteScalar(cmd);

if (codigo != null)

{

int valor = 0; int.TryParse(codigo.ToString(),out valor); Resultado = valor;

}

return Resultado;

}

public static bool Actualizar(ConceptoJornalizacion parametro)

{

bool Resultado = false;

Database db = DatabaseFactory.CreateDatabase(NombreInstancia);

DbCommand cmd = db.GetStoredProcCommand(SPActualizar);

Parser.ParametrosSoloActualizable<ConceptoJornalizacion>(ref db, ref cmd, parametro);

db.ExecuteNonQuery(cmd);

Resultado = true;

return Resultado;

}

public static bool Eliminar(ConceptoJornalizacion parametro)

{

bool Resultado = false;

Database db = DatabaseFactory.CreateDatabase(NombreInstancia);

DbCommand cmd = db.GetStoredProcCommand(SPEliminar);

Parser.ParametrosSoloLlavePrimaria<ConceptoJornalizacion>(ref db, ref cmd, parametro);

db.ExecuteNonQuery(cmd);

Resultado = true;

return Resultado;

}

public static List<ConceptoJornalizacion> Listar(ConceptoJornalizacion parametro)

{

Database db = DatabaseFactory.CreateDatabase(NombreInstancia);

List<ConceptoJornalizacion> resultado = null;

DbCommand cmd = db.GetStoredProcCommand(SPListar);

Parser.ParametrosSoloFiltro<ConceptoJornalizacion>(ref db, ref cmd, parametro);

IDataReader idr = db.ExecuteReader(cmd);

resultado = Parser.Query<ConceptoJornalizacion>(idr);

idr.Close();

return resultado;

}

}

}

Importante de notar en este código es que solo hay métodos y no propiedades y estos son estáticos, ya que la capa de datos no necesita guardar el estado de la clase sino solo transformar la información que viene del proveedor de los datos a una colección de entidades de negocio. Lo que se recibe en cada método es una Entidad de negocio que tiene propiedades como código, nombre, valor, dirección, etc.

Los métodos invocan store procederes en la bases de datos, o comandos de Transq-Sql estándar para los proveedores que no tengan store procederes deben de cumplir operaciones simples en la base de datos ya que la capa lógica de negocios debe de contener la complejidad del negocio, el flujo del proceso del negocio, las validaciones pertinentes de los datos que van hacer almacenados en el proveedor de los datos através de un puente, interfaz o capa la de Acceso a los Datos. Los métodos deben de reducirse a operaciones elementales como crear registros, actualizarlos, eliminarlos o mostrarlos. Cualquier derivado de estos puede estar en la siguiente capa en la del negocio. Un método admisible en esta capa puede ser el Existe que verifica únicamente si el registro en el proveedor de datos existe o no, y otros métodos muy particulares que cumplan tareas muy especificas como cálculos. Normalmente estas operaciones son denominadas CRUD de sus siglas en ingles (Create, Read, Update, Delete). Entre mas simples sean las operaciones que el proveedor de los datos realice estaremos trasladando a la capa de la lógica del negocio dicha complejidad, con ello garantizamos que en un solo lugar definimos esta lógica de flujo y reglas y no en la que me sirve únicamente para comunicarme con el proveedor de los datos. Esto trae otro beneficio intrínseco y es que no necesitamos programadores que tengan mucho conocimiento en base de datos sino en el lenguaje que desarrollando porque trasladamos la complejidad y lógica de negocio a la capa superior.

Los parámetros deben de poderse filtrar según sea el caso a través de la nulidad que nos proveed la versión 2.0 del .Net Framework, para que una operación fundamental de actualización pueda servir para cambiar el estado a un documento de ingresado a emitido o de emitido a anulado, utilizamos el mismo método para realizar distintas operaciones, si es necesario actualizar otras entidades o procesos del negocio la capa superior se encargará de ello.

Algo importante que aclarar es que estas reglas no se aplican para reportes o consultas que necesariamente necesitan aprovechar el motor del proveedor de los datos, sino para operaciones de mantenimiento y procesos del negocio.

En resumen en la capa de datos podemos decir:

1) No guarda el estado (stateless)

2) Sus parámetros deben de ser Entidades de Negocio (Clases que si tiene el valor de cada propiedad, ejemplo: Cliente, Factura, nomina, etc.).

3) Los métodos mas comunes se denominan CRUD (Crear, Leer, Actualizar o Eliminar) y realizan operación elementales en el proveedor de los datos.

4) No debe de existir ninguna validación ni operaciones complejas en esta capa solo las pertinentes para operar con el proveedor de los datos, como es el manejo de los nulos en los parámetros y en los datos que se reciben del proveedor.

Recomendaciones: Utilizar los estándares de nombramiento CAMEL, PASCAL y la forma de operar de cada método se busque un patrón estándar, por ejemplo que el crear pueda devolver el número o código creado y que se haga a través de un parámetro output o un SELECT @@IDENTITY después de la inserción del registro en el procedimiento almacenado del proveedor de los datos. Una excelente guía para el nombramiento y utilización de mayúsculas y minúsculas puedes encontrarla en el help de Visual Studio 2005:

ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.NETDEVFX.v20.en/dv_fxdesignguide/html/5fbcaf4f-ea2a-4d20-b0d6-e61dee202b4b.htm

Espero haber despertado el interés por la programación en capas y les sea útil en sus labores.

Hasta pronto,

Manolo Herrera

Pd. La Clase Parser, no es parte de los estandares de Microsoft sino que son propios que utilizo para pasar los valores de las entidades a los parámetros o los valores del proveedor de los datos a las entidades. En otro articulo sobre Entidad de Negocios y Parser mostraré el código y escribiré al respecto.

Recomiendo vean los otros árticulos bajo diseño y arquitectura de mi blog. Los interesados en ver código sobre lo que he escrito aunque algo viejo les dejo la dirección a continuación: http://enginedal.codeplex.com/