miércoles, 28 de febrero de 2007

Proxima Reunión de la Comunidad de Desarrolladores 7 de marzo de 2006

Tema:  Windows Card Space 

Expositor:  Cristian Prieto (Miembro del Buró de Expositores de la Comunidad .NET en Guatemala  & Software Architect) 

Fecha:  Miércoles  7  de  Marzo de  2007 

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

Lugar:  Hotel Intercontinental  Salón Cortez

Costo: Gratis

Otros: Habrá Coffee Break, Parqueo  y Premios

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

 

Asiste y no te pierdas el cierre de la introducción de las nuevas tecnologias del .net Framework 3.0.  Invita a tus amigos ya que tenemos lugar para todos habrás mas libros para rifar y tu puedes ser el ganador!.  

 

Atentamente,

 

Manolo Herrera

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

miércoles, 21 de febrero de 2007

Server did not recognize the value of HTTP Header SOAPAction

Este error es muy común cuando hacemos cambios y no estamos concientes de lo que nos puede afectar dichos cambios.

Los webservices tienen un namespace como este:

[

WebService(Namespace = "http://MiCompania.com.gt/" )]
Normalmente cuando creamos el archivos asmx nos crea el namespace por default.

[

WebService(Namespace = "http://tempuri.org/")]


Y se nos solicita se lo cambiemos al nombre de nuestra compañia o un nombre que nos identifique. Lo que me paso fue que una aplicacion hacia referencia a unos web services, luego en busqueda de minimizar el tiempo de compilación y con el nuevo sp1 de VS 2005 cambie el proyecto a un proyecto Web en vez de un web site y tuve al final que crear cada web services y copiar el codigo de cada uno de ellos pero solo copie la parte donde definie los [webmethod]'s, y como por default al crear los nuevos archivos asmx le asigna el namespace tempuri.org, cuando ejecute la aplicación que ya funcionaba, dejo de hacerlo con el mensaje:

Server did not recognize the value of HTTP Header SOAPAction

Encontre la solución en la web y aquí les va la referencia del sitio: http://bluebones.net/2003/07/server-did-not-recognize-http-header-soapaction/


La solución entonces para mi caso fue cambiar los namespace por default al que apuntaba mi aplicación y listo continuo funcionando, como lo explica el link que les comparto.
Ojo!: cuando actualice los web reference del proyecto que me dejo de funcionar me actualizo los archivos disco haciendo referencia a tempuri.org y no al de mi compañia, lo cual es mas correcto.

--
Saludos,

Manolo Herrera

martes, 20 de febrero de 2007

Capa Lógica de Negocios

Aquí les comparto este articulo que les comparti a los amigos de MSCoder.
Introducción

Hoy en día todos los desarrolladores comprendimos que es necesaria una capa intermedia entre nuestra aplicación y el proveedor de los datos, conocida como "Capa de Acceso a los Datos", pero muy pocos han comprendido las implicaciones de continuar desarrollando aplicaciones que contengan "súper botones" que en la capa de presentación tiene toda la lógica del negocio. El problema de este enfoque es que estaremos desarrollando aplicaciones para el día, día sin una planificación que nos permita construir una infraestructura de software que nos permita disponer de servicios que puedan ser reutilizados y extendidos por las aplicaciones existentes o nuevas. Otro problema de no desarrollar aplicaciones con arquitectura de n-capas es que se abre una brecha o GAP de seguridad cada vez que necesitamos reescribir el código para otra plataforma sin garantizar que la "misma" aplicación se comporte igual sin importar la plataforma o dispositivo en que se desarrolla la nueva capa de presentación de nuestra aplicación.

Este articulo ha sido escrito asumiendo que ya se ha tenido alguna experiencia desarrollando aplicaciones con C# , ya que no explica el uso del lenguaje sino la aplicación de la arquitectura n-capas en aplicaciones desarrollados en la plataforma .NET Framework 2.0.

Definición

El tener una arquitectura n-capas bien definida reducirá los hoyos de vulnerabilidad que evidentemente se dan cuando no desarrollamos con esta arquitectura. Dentro de esta arquitectura la capa intermedia o la capa lógica de negocios ocupa un lugar angular en la construcción de una infraestructura de software que nos permitirá el crecimiento y la extensibilidad de servicios para todas las aplicaciones existentes y futuras.

La definición de los linderos de cada capa nos permitirá definir correctamente la colaboración que proveerá cada una de ellas y descubriremos que la capa intermedia es inevitablemente la lógica de negocios, esto hará una infraestructura robusta y lista para la extensibilidad y el crecimiento como proveedora de servicios. Veámoslo más a detalle:

La capa de acceso a los datos su propósito primario es separar al proveedor de datos del resto de la aplicación. Hoy en día gracias a la tecnología que nos provee el .NET Framework esta capa puede ser reutilizada para diversos proveedores sin hacer cambios significativos o prácticamente sin ningún cambio. Pero esta capa no debe de guardar ningún grado de complejidad mas que el ejecutar las operaciones fundamentales del proveedor de datos operaciones conocidas por sus siglas en inglés CRUD (Creación, Lectura, Actualización y Eliminación). Las Métodos de esta capa debe de realizar operaciones mono transaccionales, o atómicas. Ya que la capa superior, la intermedia se encargará de manejar las transacciones gracias a la disponibilidad del System.Transactions del .NET Framework 2.0 de la clase TransactionScope. He aquí la colaboración entre las capas, la capa de acceso a datos se convierte en una capa un sencilla gracias a que la capa intermedia se encargará de las transacciones y esto gracias a la tecnología disponible que tenemos en el .NET Framework 2.0.

A continuación mostraré un segmento de código que muestre lo expuesto.

public static class ConceptoTransaccionBLL

{

private const decimal FactorCalculoImpuesto = 0.10141728;

public const string CrearExplicacion = "Proceso bla, bla bla";

public static int? Crear(ConceptoTransaccion parametro)

{

int? Resultado = null;

if (ValidarCrear(parametro)

{

using (TransactionScope ts = new TransactionScope())

{

Resultado = ConceptoTransaccionDAL.Crear(parametro);

parametro.Nombre = "Prueba de Transaccion";

parametro.ValorImpuesto = parametro.Valor * FactorCalculoImpuesto;

OtraTransaccionDAL.Actualizar(parametro);

ts.Complete();

}

}

return Resultado;

}

}

Este segmento de código típico de la capa intermedia o de negocios nos muestra lo siguiente:

El flujo del proceso del negocio:

Como el ejemplo anterior que define que luego de crear El Concepto de la Transacción, Actualizará Otra Transacción.

Resultado = ConceptoTransaccionDAL.Crear(parametro);

OtraTransaccionDAL.Actualizar(parametro);

Las reglas del negocio:

En nuestro segmento de código en medio del proceso realizamos el calculo de un impuesto X.

private const decimal FactorCalculoImpuesto = 0.10141728;

….

parametro.ValorImpuesto = parametro.Valor * FactorCalculoImpuesto;

Para facilitar el ejemplo se declaró como una constante este factor pero bien pudo estar almacenada esta información en el proveedor de datos u en otro medio.

Validaciones de los datos:

Esta capa debe de garantizar que los datos requeridos para procesarla fueron debidamente validados en el ejemplo anterior encapsulamos estas validaciones en el método validar, y solo si es exitosa la validación puede iniciar el flujo del proceso del negocio.

if (ValidarCrear(parametro){{…}}

Textos de explicación de los procesos del negocio:

Estos textos aunque pertenecen a esta capa pueden ser útiles en la capa de presentación para informar al usuario de que se trata el proceso que ha seleccionado ejecutar.

public const string CrearExplicacion = "Proceso bla, bla bla";

Manejo de transacciones:

En esta capa utilizamos la librería de .NET Framework 2.0, System.Transactions para manejar las transacciones que se adaptan muy bien si en la capa de acceso a los datos estoy utilizando Las librerías de Práticas y Patrones " Microsoft Enterprise Library 2.0 de enero del 2006" que utiliza las mejores prácticas de acceso a los datos de la tecnología ADO.NET 2.0. La clase TransactionScope hace todo el trabajo por nosotros. Además podemos definir Opciones para variar el comportamiento del alcance de las transacciones, lo cual por simplificación no expondremos aquí pero lo invito a que investigue en la Red.

using (TransactionScope ts = new TransactionScope())

{

ts.Complete();

}

Ts.Complete() si llega ejecutarse realiza el commit o persistencia "física" en el proveedor de los datos, si se interrumpe el código antes de ejecutar esta sentencia, entonces hará un rollback(revertira lo actualizado en el proveedor de datos) de las transacciones que hasta ese momento se ejecutaron. Esta fuera del alcance de este documento enseñar a los lectores el manejo de transacciones, pero si es mi objetivo dejar una inquietud de conocer a detalle el manejo sencillo y eficiente de las transacciones en el .NET Framework 2.0.

Facilita su lectura y manejo en la Capa de Presentación

Por lo tanto ya que hemos encapsulado la lógica en la capa intermedia, en la capa de presentación solo tendremos que llenar la clase que representa la entidad de negocio e invocar el método de la capa de negocios para procesar la información como se muestra en el siguiente segmento de código:

private void Grabar()

{

try

{

Factura parametro = this.Factura;

parametro.UsuarioSistema = this.User.Identity.Name;

TransaccionBLL.Crear(parametro);

MessageBox.Show("Operacion realiza con Exito!");

}

catch (Exception ex)

{

MessageBox.ShowError(ex.Message);

}

}

En este segmento invocamos un Método en la capa de presentación Grabar que a su vez, invoca de la capa de negocio TransaccionBLL y su método Crear que recibirá como único parámetro la entidad Factura. Y eso es todo.

La entidad del negocio

Es muy importante definir la estratégica de comunicación entre las capas. En el ejemplo expuesto hemos utilizado Entidades de Negocio, representadas por Clientes, Facturas, Empleados, Nomina Quincenal, Movimientos Bancarios, etc.

Estas entidades carecen de métodos solo tiene propiedades, campos y posiblemente atributos, que nos pueden servir para almacenar la información como el mapeo de las propiedades a las columnas de la base de datos. Luciría de la siguiente manera:

[ ConnectionStringName("NORTHWIND"), ReadMethodName("OrderRead")]

public class Orden

{

private int? numeroOrden;

[ColumnName("OrderID"), ParameterName( "@OrderID",IsPrimaryKey=true,IsForCreate=false)]

public int? NumeroOrden

{

get { return numeroOrden; }

set { numeroOrden = value; }

}

private DateTime fecha;

[ColumnName("OrderDate"), ParameterName( "@OrderDate")]

public DateTime Fecha

{

get { return fecha; }

set { fecha = value; }

}

}

Donde ConnectionStringName, ReadMethodName, ColumnName, ParameterNamel, son atributos personalizados que el programador puede definir y que el .NET Framework utiliza ampliamente desde su versión 1.0. Se recuerda del [Seriealizable] este es un atributo que indica al CLR que la clase puede persistir en algún medio utilizado como lo es una variable de sessión, o una variable ViewState en ASP.NET.

Ahora veamos un ejemplo paso a paso de transformación de un súper botón a una implementación de arquitectura de n-capas.

A continuación les mostrare un ejemplo de un segmento de código que representa el evento de un botón que tiene como propósito insertar una orden de un cliente en la base de datos "Northwind" (incluida en SQL Server 2000). El código lo iremos transformando hasta alcanzar la infraestructura de negocios aplicando los conceptos de arquitectura de n-capas en .NET Framework 2.0.

private void btnInsertar_Click(object sender, EventArgs e)

{ // Validaciones de los datos capturados

if (parametroFechaOrden == null parametroFechaOrden == DateTime.MinValue)

{

MessageBox.Show("Debe de ingresar el numero de la Orden" );

return;

}

if (paremetroCodigoCliente == null paremetroCodigoCliente.Length == 0)

{

MessageBox.Show("Seleccione el cliente");

return;

}

// Segmento de Codigo usual en el manejo de la Capa de Presentacion

this.dataGridView1.Enabled = false; this.btnConsultar.Enabled = false;

// Manejo del acceso a los datos

Database db = DatabaseFactory .CreateDatabase("NORTHWIND");

string sqlCommand = "OrderCreate";

DbCommand orderCommand = db.GetStoredProcCommand(sqlCommand);

db.AddInParameter(orderCommand, "@CustomerID", DbType.String, paremetroCodigoCliente);

db.AddInParameter(orderCommand, "@EmployeeID", DbType.Int32, parametroCodigoEmpleado);

db.AddInParameter(orderCommand, "@OrderDate", DbType.Date, parametroFechaOrden);

sqlCommand = "OrderDetailCreate";

DbCommand orderDetailCommand = db.GetStoredProcCommand(sqlCommand);

using (DbConnection connection = db.CreateConnection())

{

connection.Open();

//manejo de transacciones

DbTransaction transaction = connection.BeginTransaction();

try

{

object valor = db.ExecuteScalar(orderCommand, transaction);

int numeroOrden = 0;

if (!int.TryParse(valor.ToString(), out numeroOrden)) throw new Exception("No pudo obtener el numero de Orden");

foreach (OrdenLinea linea in ordenDetalle)

{

if (orderDetailCommand.Parameters.Count > 0)

orderDetailCommand.Parameters.Clear();

db.AddInParameter(orderDetailCommand, "@OrderID", DbType.Int32, numeroOrden);

db.AddInParameter(orderDetailCommand, "@ProductID", DbType.String, linea.CodigoProducto );

db.AddInParameter(orderDetailCommand, "@UnitPrice", DbType.Decimal, linea.PrecioUnitario );

db.AddInParameter(orderDetailCommand, "@Quantity", DbType.Int32, linea.Cantidad );

db.AddInParameter(orderDetailCommand, "@Discount", DbType.Decimal, linea.Descuento);

db.ExecuteNonQuery(orderDetailCommand, transaction);

}

transaction.Commit();

}

catch (Exception ex)

{

transaction.Rollback();

MessageBox.Show(ex.Message);

this.dataGridView1.Enabled = true; this.btnConsultar.Enabled = true;

return;

}

finally

{

connection.Close();

}

this.dataGridView1.Enabled = true; this.btnConsultar.Enabled = true;

MessageBox.Show( "Ordern Guardada!");

}

}

El código anterior muestra un código típico cuando "no" se utiliza la arquitectura de n-capas, en el desarrollo de aplicaciones empresariales. Es muy difícil interpretar este código porque incluye el manejo de la capa de presentación las validaciones de los datos, el manejo de errores. Adicional a ello si necesitamos realizar esto en otra opción en nuestra aplicación estaremos obligados a repetir el código, y entonces la brecha (GAP) se expande en ves de reducirse en el objetivo de minimizar las entradas maliciosas a nuestra aplicación. Este es otro aspecto el de Seguridad es el que nos obliga a tener una infraestructura de software bien definida para reducir los puntos de vulnerabilidad de nuestra aplicación.

Ahora veamos como quedaría nuestra aplicación si refactorizamos (la disciplina que promueve la mejora del código interno sin afectar el comportamiento externo de una aplicación. Uno de sus expositores mas famosos es Martin Fowler y su libro "Refactoring") este código e implementamos la arquitectura de n-capas:

private void btnInsertar_Click(object sender, EventArgs e)

{

// Segmento de Codigo usual en el manejo de la Capa de Presentacion

this.dataGridView1.Enabled = false; this.btnConsultar.Enabled = false;

Orden orden = new Orden();

orden.CodigoCliente = paremetroCodigoCliente;

orden.Fecha = parametroFechaOrden;

try

{

bool OperacionExitosa = OrdenBLL.Crear(orden, ordenDetalle);

if (OperacionExitosa)

MessageBox.Show("Ordern Guardada!");

}

catch (Exception ex)

{

MessageBox.Show(ex.Message);

}

this.dataGridView1.Enabled = true; this .btnConsultar.Enabled = true;

}

Podemos notar que ahora es mucho mas fácil entender el código para hemos encapsulado en la capa intermedia la de negocios todo el código referente a la lógica del negocio. Deducimos por simple observación que hay una clase "OrdenBLL" que tiene un Método Crear; que Crea la orden (el encabezado y detalle de la orden), alrededor esta el código propio de la capa de presentación y el manejo del error, que para ejemplo solo lo capturamos para evitar una ruptura en el funcionamiento de la aplicación y mostramos el error.

Ahora vayamos mas adentro del código y veamos que encierra el método Crear de la clase "OrdenBLL":

public class OrdenBLL

{

public static bool Crear(Orden orden, OrdenLinea[] ordenDetalle){..}

public static bool Validar(Orden orden){…}

}

Esta clase de la capa intermedia tiene dos métodos uno que crea la orden y otro que valida los valores de la entidad de negocio Orden. Veamos ahora el método crear:

public static bool Crear(Orden orden, OrdenLinea[] ordenDetalle)

{

bool OperacionExistosa = false;

bool CreoDetalle = false;

if (Validar(orden))

{

using (TransactionScope ts = new TransactionScope ())

{

int? numeroOrden = OrdenDAL.Crear(orden);

if (numeroOrden == null numeroOrden == 0)

throw new Exception("No pudo obtener el numero de Orden" );

foreach (OrdenLinea linea in ordenDetalle)

{

linea.NumeroOrden = numeroOrden;

CreoDetalle = OrdenDAL.CrearDetalle(linea);

if (CreoDetalle) break ;

}

if (CreoDetalle)

ts.Complete();

}

}

return OperacionExistosa;

}

Este método se encarga de invocar el método validar que es publico por si se desea validar los datos sin efectuar ninguna operación. Luego de validar los datos esta capa se encarga de manejar las transacciones ya que invocamos dos diferentes métodos de la capa de acceso a datos Y cada uno de ellos maneja su propia conexión. Pero el código es fácilmente entendible indicándonos que hay una clase "OrdenDAL" que tiene un método para crear la orden (el encabezado) y otro el detalle si sucede algo sabemos que esta manejando transacciones y revertirá todo el proceso.

Ahora veamos los métodos de la capa de acceso a datos. Iniciemos con el método que Crea el encabezado de la orden:

public class OrdenDAL

{

public static int? Crear(Orden orden)

{

int? NuevoNumeroOrden = null;

Database db = DatabaseFactory .CreateDatabase("NORTHWIND");

string sqlCommand = "OrderCreate";

DbCommand orderCommand = db.GetStoredProcCommand(sqlCommand);

db.AddInParameter(orderCommand, "@CustomerID", DbType.String, orden.CodigoCliente);

db.AddInParameter(orderCommand, "@EmployeeID", DbType.Int32, orden.CodigoEmpleado);

db.AddInParameter(orderCommand, "@OrderDate", DbType.Date, orden.Fecha);

DbCommand orderDetailCommand = db.GetStoredProcCommand(sqlCommand);

using (DbConnection connection = db.CreateConnection())

{

connection.Open();

//manejo de transacciones

try

{

object valor = db.ExecuteScalar(orderCommand);

int numeroOrden = 0;

int.TryParse(valor.ToString(), out numeroOrden);

}

finally

{

connection.Close();

}

}

return NuevoNumeroOrden;

}

Esto método solo se encarga de pasar los valores de la entidad y ejecutar el procedimiento almacenado. A continuación el método que crea el detalle de la orden:

public static bool CrearDetalle(OrdenLinea detalle)

{

bool operacionExitosa = false;

Database db = DatabaseFactory.CreateDatabase("NORTHWIND" );

string sqlCommand = "OrderDetailCreate";

DbCommand orderDetailCommand = db.GetStoredProcCommand(sqlCommand);

db.AddInParameter(orderDetailCommand, "@OrderID", DbType.Int32, detalle.NumeroOrden);

db.AddInParameter(orderDetailCommand, "@ProductID", DbType.String, detalle.CodigoProducto );

db.AddInParameter(orderDetailCommand, "@UnitPrice", DbType.Decimal, detalle.PrecioUnitario );

db.AddInParameter(orderDetailCommand, "@Quantity", DbType.Int32, detalle.Cantidad);

db.AddInParameter(orderDetailCommand, "@Discount", DbType.Decimal, detalle.Descuento);

int afectados = db.ExecuteNonQuery(orderDetailCommand);

operacionExitosa = afectados > 0;

return operacionExitosa;

}

}

Este método la única diferencia del anterior es que crea el detalle de la orden pero hace lo mismo pasa los valores de la entidad y ejecuta el procedimiento almacenado. Esta capa gracias a la abstracción de la capa intermedia se convierte en una capa que realiza operaciones atómicas, mono-transaccionales, esto facilita su comprensión y reduce el riesgo de un error en el código porque solo hace una casa pasar los datos de la entidad al proveedor de datos. Si hay una falla del lado del proveedor de datos sabemos que es en esta capa o en el mismo proveedor de datos y no disperso por toda la aplicación o la capa de presentación.

El Futuro

Por ultimo quisiera mencionar que hay una fuerte tendencia y mala concepción que la lógica de negocio es más fácil y práctica dejarla en el proveedor de los datos, pero no es así. Si analizamos un poco mas encontraremos que si tenemos un lenguaje orientado a objetos como lo es C#, con disposición a definir Tipos Genéricos, la capacidad de utilizar la reflexión en las entidades, la definición de atributos, podremos reutilizar el código, cambiar su comportamiento, heredar los objetos y abstraer y encapsular el código de una forma mucho mas fácil y eficiente que desde un proveedor de datos relacional. En especial si vemos que en el futuro podremos invocar y manejar las colecciones como lo haríamos desde un proveedor de datos de una forma mas natural o través de funciones extensibles como lo serán el FROM, WHERE y SELECT dentro del mismo lenguaje de programación porque el .NET Framework 3.0 se encargará de generar el código al proveedor de datos o no ha oído o leído sobre LINQ. Aquí le dejo un segmento de código para que se emocione e investigue más al respecto:

from c in customers
join o in orders on c.CustomerID equals o.CustomerID into co
let n = co.Count()
where n >= 10
select new { c.Name, OrderCount = n }

Código extraído de las especificaciones del Nuevo C# 3.0 Mayo 2006.

Resumen

En resumen si tenemos definida la capa intermedia y su colaboración con la capa de acceso a los datos hacia abajo y hacia arriba la capa de presentación. Si fuera necesario que lo es hoy en día proveer a los usuarios de nuestra aplicación de acceso a otra plataforma o dispositivo de Hardware, solo nos preocuparíamos por crear una nueva capa de presentación y a lo sumo actualizar o modificar la capa de acceso a datos para acceder un proveedor mas liviano para implementar nuestra aplicación en un dispositivo móvil. Pero ya no nos preocuparíamos del negocio en sí, sino de la parte tecnológica que es la parte donde somos más competentes. Esto al largo plazo nos dará mayor seguridad y preparación para enfrentar los retos presentes y futuros, porque hemos desarrollado una infraestructura de software con servicios bien definidos gracias a la implementación de la arquitectura de n-capas.

Hasta una próxima vez!

Manolo Herrera

Líder de la Comunidad de Desarrolladores .NET en Guatemala