lunes, 5 de mayo de 2008

WSE Parte I


Web Service Enhancements.

Antes de iniciarnos con WSE, debemos entender que es un web service, ya que WSE, como su nombre lo indica, trabaja sobre WebServices y realza o amplia los protocolos de transferencia y transmisión de datos, así como la seguridad entre un websrvice y una aplicación.

Para poder utilizar WSE, lo primero que debemos hacer es descargarnos esta librería e instalarla en el servidor en el cual se ejecutarán los WebServices y en el PC en donde se desarrollará la aplicación, esta librería se la pueden descargar desde aquí



WebService.


Un webservice en un servicio que una organización entrega a sus clientes a través de la web, ya sea en canales de Internet o de Intranet, para ser consumidos en aplicaciones web y/o aplicaciones Windows. Cuando hablo de aplicaciones Windows, me refiero a cualquier tipo de aplicación que se puede crear en plataforma Windows y que puede consumir web services.


Los web services contienen métodos que se llaman desde las aplicaciones que consumen estos web services, estos métodos son conocidos como métodos web y son definidos como cualquier otro método de una aplicación, con la salvedad de que estos llevan la etiqueta de firma que los define como métodos web (Web Method).


Bueno ahora debemos saber que un Web Method de un Web Service se pude consumir de dos forma: Sincrona o Asíncrona.

  • Cuando solicitamos directamente un método y la aplicación queda esperando respuesta (no permite seguir operando en la interfaz) estamos hablando de que estamos realizando una llamada sincrona.
  • Cuando solicitamos un método de forma asincrona, la aplicación permite que se realicen otras acciones en la interfaz hasta que el Web Method del Web Service responde a la aplicación.


Dependiendo del canal de transferencia y de la velocidad de respuesta del metodo web, es que utilizaremos uno u otro.

En esta primara parte veremos como ejecutar un Web Method de un Web Service de forma sincrona y asíncrona, luego cuando veamos el comportamiento del Web Services, veremos WSE, que permite otros tipos de forma de transferencia de datos.

Los casos de WSE que veremos en la segunda parte estarán orientados a SOAP y a Enrutamiento (esto es bueno cuando deseas enmascarar el web services original).


Ahora al web services simple con WSE.

El ejercicio que presento a continuación tiene incluída la librería de WSE, así que recomiendo que se descargen WSE 3.0 y lo instalen antes de seguir con el ejercicio:


Los ejercicios que vamos a hacer están rescatados de los ejemplos que vienen en WSE 3.0 que se instalan con la librería, pero como siempre, en esta ocación vienen explicados paso a paso:


1 - Antes de realizar cualquier acción para la creación del WebServices, crearemos el proyecto de cliente que consumirá el WS, esto nos permitirá agregar el proyecto WS a la carpeta de solución, el proyecto de Windows se llamará WseWinBasico. Luego crearemos una aplicación de web services que llamaremos WseWsvBasico, dentro de este proyecto, eliminaremos el servio web por defecto y agregaremos un Servicio llamado WseEjercicioUNO.


2 - Como siguiente paso realizaremos la instanciación a las dll que nos permitirán utilizar los Servicios de WSE:

Nos posicionaremos en el nodo raíz del proyecto y seleccionaremos con el botón derecho la opción Agregar Referencias, como se muestra a continuación.




Al abrirse la ventana que nos permitirá agregar las referencias, buscaremos las referencias a la librería Microsoft.Web.Service3, como se muestra en la siguiente imagen:



Presionar el botón aceptar, con esto se observará que se ha agregado la referencia a esta librería, viendo en las propiedades del proyecto de WebService.

3 - El siguiente paso será agregar la instanciación de la librería en el proyecto, para esto agregaremos la siguiente línea de código en la cabecera de directivas:

using Microsoft.Web.Services3;

4 - Adicionalmente, agregaremos una llamada de instanciación a la librería:

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


Estas librería nos permitirán utilizar las opciones de generación de listas y la generación de archivos XML, tanto de llamadas a métodos como de respuestas de los mismos.

5 - Como siguiente paso, definiremos una estructura que nos permitirá retornar información al cliente que consuma el servicio. Esta estructura retornará valores de tio xml y se definirá de la siguiente forma:

[XmlRoot(Namespace = "http://wse/Ejemplo001")]
public class Usuario
{
public String Valor;
public String Apellido;
public String Nombre;
public double Sueldo;
public DateTime fecha;
}

Esta estructura será definida fuera del espacio de nombre de la definición del Web Service. Si no tenemos habilitada la instanciación a System.Xml.Serializacion y System.Collections.Generic no se podrá definir la estructura de retorno.

6 - El siguiente paso será construir el método que capturará la información que se envía desde el cliente, para ello definiremos las siguientes directrices que estarán asociadas al método:

[WebMethod]

Asociado a todos los métodos que se presentarán como disponibles dentro de un servicio web.

[SoapDocumentMethod(ResponseElementName = "Usuario")]

Al aplicar SoapDocumentMethod, a un método se especifica que los mensajes SOAP hacia y desde el método utilizan el formato Document.ResponseElementName obtiene o establece el elemento asociado a la respuesta SOAP de un método de servicios Web XML.

[return: XmlElement("Usuario")]

Define que el parámetro de retorno será enviado a través de SOAP.

7 - Definiremos el método de la siguiente forma:

public List UsuarioDatoSolicitud([XmlArray(IsNullable = true), XmlArrayItem("Valor", IsNullable = false)] string[] Valores)
{
}

Si se observa el método tiene una estructura de lista, como la que se ha definido.

8 - Dentro del método definiremos las siguientes líneas de código:

List<Usuario> usuarioDato = new List<Usuario>();

foreach (String valor in Valores)
{
Usuario usu = new Usuario();
usu.Valor = valor;
if (valor == "m.roa")
{
usu.Nombre = "Mario Antonio";
usu.Apellido = "Roa Vidal";
usu.Sueldo = 5.5;
}
else
{
usu.Nombre = "Walter Borney";
usu.Apellido = "Roa Cleary";
usu.Sueldo = 1.15;
}
usuarioDato.Add(usu);
}
return usuarioDato;



Con esto ya tenemos listo el Método Web del Web Service para ejecutarlo. Si ejecutamos el Web Service observaremos que nos solicitará definir el modo de depuración dl WebService, a lo cual diremos que si.



Se podrá observar que la aplicación Web, no solicita parámetros, aunque tenga definido parámetros de entrada, lo que permite ver son las solicitudes de llamada y las estructura de retorno del Metodo Web.



I - CONSUMO DEL SERVICIO WEB DESDE UNA APLICACIÓN WINDOWS SINCRONICAMENTE


Ya creado el servicio web, debemos realizar el consumo del mismo para ello crearemos una referencia web al webservices que llamaremos WseWsv, y luego crearemos en la aplicación windows una interfaz de tipo winforms, luego agregaremos tres botones, un list box, tres controles label y tres textbox, a la interfaz que hemos definido en nuestra aplicación Windows, quedando la internas de la siguiente forma:



Con esto definido, podremos seguir con los siguientes pasos:

1 - En el proyecto Windows , agregaremos la referencia a la librería Microsoft.Web.Service3

2 - Agregaremos una definición de uso de la referencia Web dentro del archivo de código del formulario, de la siguiente forma:

using WseWinBasico.WseWsv;

3 - Dentro del espacio de nombre de la solución de la aplicación agregaremos una referencia al servicio web que utilizaremos, como se muestra a continuación:

// definimos la variable de instancia
WseEjercicioUno wsvProxi;


4 - Adicionalmente definiremos una variable de tipo GUID que utilizaremos en la llamada asincrona que realizaremos en previos pasos de la siguiente forma:

Guid guid;

También debemos agregar una referencia al WebService que vamos a consumir.

5 - En el método que atiende el evento load del formulario definiremos las siguientes líneas de código:

//cargamos el listbox
this.lbxUsuario.Items.Add("m.roa");
this.lbxUsuario.Items.Add("w.roa");

// creamos la instancia
wsvProxi = new WseEjercicioUno();

// usar una guía para el objeto
// UserState en vez de cancelar la solicitud
guid = new Guid();

this.btnCerrar.Enabled = false;

6 - Por ultimo, en el método que atiende el evento clic del botón de llamada sincrónica, definiremos las siguientes líneas de código:

this.btnSincrono.Enabled = false;

String[] symbols = new String[1];
symbols.SetValue((string)this.lbxUsuario.SelectedItem, 0);

try
{
Usuario[] usuario = wsvProxi.UsuarioDatoSolicitud(symbols);
foreach (Usuario usu in usuario)
{
this.txtNombre.Text = usu.Nombre;
this.txtApellido.Text = usu.Apellido;
this.txtSueldo.Text = usu.Sueldo;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
this.btnSincrono.Enabled = true;
}


7 - Ejecutamos la solución y observaremos el comportamiento del Web Service Sincrónico



II - CONSUMO DEL SERVICIO WEB DESDE UNA APLICACIÓN WINDOWS ASINCRONICAMENTE


Los llamados asincrónicos de WebServices tienen un comportamiento similar los de Hilos, en donde se realiza la llamada a un web services y este responde en cuanto se termine la solicitud. Para definir una llamada a un Web Service de forma asincrónica se deben seguir los siguientes pasos:

1 - Antes de realizar cualquier acción sobre el método que atiende el evento clic del bot´n de llamada asincrónica, se debe crear el siguiente método:

// llamada asincrónica al evento
private void WsvProxyUsuario(object sender, UsuarioDatoSolicitudCompletedEventArgs e) {

}

2 - Dentro del método recién creado agregaremos las siguientes líneas de código:

//inicializamos los estados de los botones
this.btnAsincrono.Enabled = true;
this.btnSincrono.Enabled = true;
this.btnCerrar.Enabled = false;

if (e.Error != null)
{
// llamada fallida
MessageBox.Show("La llamada al Web Service a Fallado");
}
if (e.Cancelled)
{
// llamada cancelada
MessageBox.Show("La llamada al Web Service a sido Cancelada");
}
else
{
// conexión exitosa
Usuario[] usuario = e.Result;
foreach (Usuario usu in usuario)
{
this.txtNombre.Text = usu.Nombre;
this.txtApellido.Text = usu.Apellido;
this.txtSueldo.Text = usu.Sueldo.ToString();
}
}


3 - En el método que atiende el evento clic del botón de llamada asincrónica, agregaremos las siguientes líneas de código.

this.btnAsincrono.Enabled = false;
this.btnSincrono.Enabled = false;
this.btnCerrar.Enabled = true;
this.txtNombre.Text = "";
this.txtApellido.Text = "";
this.txtSueldo.Text = "";

// asignación de llamada al método asincrónico
wsvProxi.UsuarioDatoSolicitudCompleted
+= new UsuarioDatoSolicitudCompletedEventHandler(WsvProxyUsuario);

// asignación de variable de llamada al método asincrónico
String[] symbols = new String[1];
symbols.SetValue((string)this.lbxUsuario.SelectedItem, 0);

// llamada asincrona del método
wsvProxi.UsuarioDatoSolicitudAsync(symbols, guid);

4 - Por ultimo, si se observa, hemos definido una variable de tipo Guid, la cual nos permitirá controlar la llamada y cancelación de la consulta asincrónica, por lo cual, podremos definir en el método que atiende el botón cerrar llamada asincrónica la siguiente línea de código.

// se cancela la llamada asincrona
wsvProxi.CancelAsync(guid);



III - CONSUMO DE SERVICIOS WEB DE FORMA ASINCRONA EN APLICACIONES WEB



Para desarrollar una interfaz que consuma el método del servicio web definido, se deben seguir los siguientes pasos:

1 - Crearemos una interfaz que tenga la siguiente apariencia:






2 - En el proyecto, agregaremos la referencia a la librería Microsoft.Web.Service3

3 - Agregaremos una definición de uso de la referencia Web dentro del proyecto al servicio web de la siguiente forma:

using WseWinBasico.WseWsv;

4 - Dentro del espacio de nombre de la solución de la aplicación agregaremos una referencia al servicio web que utilizaremos, como se muestra a continuación:

// definimos la variable de instancia
WseEjercicioUno wsvProxi;


Adicionalmente definiremos una variable de tipo GUID que utilizaremos en la llamada asincrona que realizaremos en previos pasos de la siguiente forma:

Guid guid;

También debemos agregar una referencia al WebService que vamos a consumir.

5 - En el método que atiende el evento load del formulario definiremos las siguientes líneas de código:

//cargamos el listbox
this.lbxUsuario.Items.Add("m.roa");
this.lbxUsuario.Items.Add("w.roa");

// creamos la instancia
wsvProxi = new WseEjercicioUno();

// usar una guía para el objeto
// UserState en vez de cancelar la solicitud
guid = new Guid();

this.btnCerrar.Enabled = false;


6 - Antes de realizar cualquier acción sobre el método que atiende el evento clic del botón de llamada asincrónica, se debe crear el siguiente método:

// llamada asincrónica al evento
private void WsvProxyUsuario(object sender, UsuarioDatoSolicitudCompletedEventArgs e)
{


}


7 - Dentro del método recién creado agregaremos las siguientes líneas de código:

//inicializamos los estados de los botones
this.btnAsincrono.Enabled = true;
this.btnSincrono.Enabled = true;
this.btnCerrar.Enabled = false;

if (e.Error != null)
{
// llamada fallida
MessageBox.Show("La llamada al Web Service a Fallado");
}
if (e.Cancelled)
{
// llamada cancelada
MessageBox.Show("La llamada al Web Service a sido Cancelada");
}
else
{
// conexión exitosa
Usuario[] usuario = e.Result;
foreach (Usuario usu in usuario)
{
this.txtNombre.Text = usu.Nombre;
this.txtApellido.Text = usu.Apellido;
this.txtSueldo.Text = usu.Sueldo.ToString();
}
}



8 - En el método que atiende el evento clic del botón de llamada asincrónica, agregaremos las siguientes líneas de código.

this.btnAsincrono.Enabled = false;
this.btnSincrono.Enabled = false;
this.btnCerrar.Enabled = true;
this.txtNombre.Text = "";
this.txtApellido.Text = "";
this.txtSueldo.Text = "";

// asignación de llamada al método asincrónico
wsvProxi.UsuarioDatoSolicitudCompleted
+= new UsuarioDatoSolicitudCompletedEventHandler(WsvProxyUsuario);

// asignación de variable de llamada al método asincrónico
String[] symbols = new String[1];
symbols.SetValue(this.lbxUsuario.SelectedItem.ToString(), 0);

// llamada asincrona del método
wsvProxi.UsuarioDatoSolicitudAsync(symbols, guid);


9 - Por ultimo, si se observa, hemos definido una variable de tipo Guid, la cual nos permitirá controlar la llamada y cancelación del la consulta asincrónica, por lo cual, podremos definir en el método que atiende el botón cerrar llamada asincrónica la siguiente línea de código.

// se cancela la llamada asincrona
wsvProxi.CancelAsync(guid);


En la cabecera de definición del código HTML se debe agregar la autorización de ejecución de las llamadas asincronas, para ello se debe definir la siguiente línea de código:

Async="true"

Quedando la cabecera HTML de llamada al ensamblado de la siguiente forma:

<%@ Page Language="C#" AutoEventWireup="true"
Async="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

WSE Parte II


No voy a perder tiempo en definirles que es WSE ya que nuestros amigos de microsoft ya tienen un sitio en donde explican este tema, por lo cual solo les pondré la url para que consulten que es WSE Ir a Explicación de WSE

Para poder utilizar WSE, lo primero que debemos hacer es descargarnos esta librería e instalarla en el servidor en el cual se ejecutarán los WebServices y en el PC en donde se desarrollará la aplicación, esta librería se la pueden descargar desde aquí

A continuación despliego la información del ejemplo:

MENSAJERÍA SOAP UTILIZANDO WSE

El presente ejercicio estará orientado a consumir desde un portal de Web Service una clase que contendrá un método que nos permitirá retornar la información que utilizaremos, para ello debemos crear tres proyectos de distinto índole, los cuales presentarán las siguientes características:

  • BILBIOTECA DE CLASE. El cual estará encargado de administrar las estructuras que se utilizarán, tanto en la aplicación de cliente como en la clase que residirá en el servicio Web. Adicionalmente se controlará el tipo de versión de cabecera SOAP que se utilizará en el enlace entre el cliente y el servidor Web.
  • WEB SERVICES. En esta aplicación residirá la clase que contendrá el método que se consumirá desde el cliente. Este portal de servicios Web no tendrá ningún servicio Web definido, solo contendrá la clase descrita.
  • CLIENTE DE CONSOLA. La cual utilizando la biblioteca de clase y la clase del Web Service, obtendrá los resultados deseados.

I - BILBIOTECA DE CLASE.

Para la creación del proyecto de solución con los proyectos antes descritos, se deben seguir los siguientes pasos:

1 - Crearemos un nuevo proyecto de bibliotecas de clases al cual llamaremos WseBase.

2 - Agregaremos las siguientes referencias a la solución.

  • Microsoft.Web.Service3
  • System.Configuration
  • System.Drawing
  • System.Security
  • System.Web
  • System.Web.Services
  • System.Windows.Forms.


3 - La clase que se crea por defecto la renombraremos de la siguiente forma Documents.cs

4 - Antes de realizar cualquier acción sobre esta clase, definiremos el uso de algunos espacios de nombres que se han referenciado o que se encuentran definidos por defecto en la aplicación, estos espacios de nombre deben ser:

using System;
using System.Xml.Serialization;


5 - Cambiaremos el nombre del espacio de nombre de la clase al siguiente Microsoft.Web.Services3.Usuario, quedando la definición del espacio de nombre de la siguiente forma:

namespace Microsoft.Web.Services3.Usuario
{
}


IMPORTANTE. Solo debe quedar definido el espacio de nombre y las definiciones de uso antes descritas en la clase creada.

6 - A continuación agregaremos las estructuras que se utilizarán dentro de la clase, estas serán:

USUARIO. Encargada de contener la estructura de definición de variables que se utilizarán en la ejecución del programa, tanto de asignación, como de despliegue de la misma.

[XmlRoot(Namespace="http://localhost/Wse")]
public class Usuario
{
public String usuario;
public String Nombre;
public String Apellido;
public double Sueldo;
}


USUARIOS. Estructura que contendrá una variable del tipo de la Clase Usuario variable.

public class Usuarios
{
[XmlElement("Usuario")]
public Usuario[] usuarios;
}

USUARIOREQUEST. Variable de tipo Usuarios, que será representada, cuando sea devuelta, en formato XML.

[XmlRoot(Namespace = "http://localhost/Wse")]
public class UsuarioRequest
{
[XmlArray("usuarios")]
[XmlArrayItem("Usuario")]
public String[] Usuarios;
}



7 - Como siguiente paso, agregaremos una nueva clase al proyecto de biblioteca de clase, la cual llamaremos AppBase. Dentro de esta clase definiremos el uso de algunos espacios de nombres que se han referenciado o que se encuentran definidos por defecto en la aplicación, estos espacios de nombre son:

using System;
using System.Collections;
using System.Configuration;
using System.Text;
using System.Net;
using System.IO;
using System.Web.Services.Protocols;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Web.Services3;
using Microsoft.Web.Services3.Configuration;
using Microsoft.Web.Services3.Security;
using Microsoft.Web.Services3.Security.Tokens;
using Microsoft.Web.Services3.Security.X509;



8 - Cambiaremos el nombre del espacio de nombre de la clase al siguiente Microsoft.Web.Services3.Usuario, quedando la definición del espacio de nombre de la siguiente forma:

namespace Microsoft.Web.Services3.Usuario
{

}


IMPORTANTE. Solo debe quedar definido el espacio de nombre y las definiciones de uso antes descritas en la clase creada.

9 - Dentro del estacio de nombre agregaremos la definición de la clase AppBase de la siguiente forma:

public class AppBase
{
}

10 - Dentro del espacio de nombre de la clase recién creada, agregaremos la siguiente definición de variable de ambiente de clase:

SoapProtocolVersion _soapVersion = SoapProtocolVersion.Default;

11 - Agregaremos, a continuación (dentro del espacio de la clase), la siguiente propiedad.

protected SoapProtocolVersion SoapVersion
{
get
{
return _soapVersion;
}
}

12 - Por último agregaremos el siguiente constructor de la clase:

// define el tipo de versión de soap que se utilizará en la llamada
public AppBase() {
string soapVersion = System.Configuration.ConfigurationManager.AppSettings["soapversion"];
switch (soapVersion)
{
case "soap11":
_soapVersion = SoapProtocolVersion.Soap11;
break;
case "soap12":
_soapVersion = SoapProtocolVersion.Soap12;
break;
default:
case "default":
_soapVersion = SoapProtocolVersion.Default;
break;
}
}

II - CREACION DE WEBSERVICE



El siguiente paso será crear el proyecto de Web Service, para ello seguiremos los siguientes pasos.

1 - Agregaremos un proyecto de Web de tipo Servicio Web a la solución, al cual llamaremos Wse. Como característica especial, definiremos que este proyecto de Web será creado en el directorio virtual de IIS, con la siguiente definición http://localhost/Wse

2 - El siguiente paso será eliminar, tanto el servicios web como su clase de componente de código asociado.

3 - A continuación agregaremos una clase a la solución que llamaremos UsuarioServicio.cs, debemos aceptar la opción que permite crear la clase para que quede disponible en el proyecto.

4 - Agregaremos las siguientes referencias al proyecto:

  • Microsoft.Web.Service3
  • WseBase
  • System.Configuration.Install
  • System.Design
  • System.Security
  • System.Windows.Form


5 - Abriremos la clase en el editor de código fuente y agregaremos las siguientes instancias de uso de espacios de nombre:

using System;
using System.Collections;

using Microsoft.Web.Services3;
using Microsoft.Web.Services3.Messaging;

using Microsoft.Web.Services3.Usuario;


NOTA: Debemos recordar que la instancia a Microsoft.Web.Service3.Usuario, está asociada a la biblioteca de clase WinBase

6 - El siguiente paso es asegurarnos que el nombre del espacio de nombre es el siguiente

namespace UsuarioServicio
{

}

7 - Dentro del espacio de nombre definido crearemos la siguiente clase:

[SoapService("http://localhost/Ejercicio002")]
public class UsuarioConsulta : SoapService
{

}

IMPORTANTE. Se debe considerar que los mismos espacios de nombre de cabecera SOAP deben ser definidos en los métodos que invocarán a la clase desde el cliente

8 - Dentro de la clase definida agregaremos la siguiente función, la cual estará encargada de retornar el XML con la lista de usuarios consultados.

[SoapMethod("http://localhost/Ejercicio002/UsuarioRequest")]
public Usuarios funUsuarioConsultar(UsuarioRequest message)
{
ArrayList list = new ArrayList();
foreach (String valor in message.Usuarios)
{
Usuario usu = new Usuario();
usu.usuario = valor;

if (valor == "m.roa")
{
usu.Nombre = "Mario Antonio";
usu.Apellido = "Roa Vidal";
usu.Sueldo = 5000;
}
else
{
usu.Nombre = "Walter Borney";
usu.Apellido = "Roa Cleary";
usu.Sueldo = 6000;
}
list.Add(usu);
}

Usuarios usuarios = new Usuarios();
usuarios.usuarios = (Usuario[])list.ToArray(typeof(Usuario));
return usuarios;
}

IMPORTANTE. Se debe considerar que los mismos espacios de nombre de cabecera SOAP deben ser definidos en los métodos que invocarán a la clase desde el cliente

9 - A continuación, reemplazaremos las líneas de código del we.config, con las siguientes:

<?xml version="1.0"?>
<configuration>
<configSections>
<section name="microsoft.web.services3" type="Microsoft.Web.Services3.Configuration.WebServicesConfiguration, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</configSections>
<system.web>
<httpHandlers>
<add verb="*" path="StockService.ashx" type="UsuarioServicio.UsuarioConsulta"/>
</httpHandlers>
<webServices>
<soapExtensionImporterTypes>
<add type="Microsoft.Web.Services3.Description.WseExtensionImporter, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</soapExtensionImporterTypes>
<webServices>
<compilation debug="true">
<assemblies>
<add assembly="Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Security, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Configuration.Install, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
</assemblies>
<compilation>
</system.web>
<configuration>



Generamos el proyecto WebService de solución

III - CREACION DE CLIENTE DE CONSOLA



Como último paso debemos crear el cliente que consumirá la aplicación, este cliente será de tipo de consola y lo agregaremos a la solución, para ello se deben seguir los siguientes pasos:

1 - Agregar un proyecto Windows de consola a la aplicación, el cual llamaremos WseConsola.

El archivo por defecto que se crea lo renombraremos con el nombre MAIN.

2 - Agregaremos las siguientes referencias a la aplicación:

  • Microsoft.Web.Service3
  • WseBase
  • System
  • System.Data
  • System.Web
  • System.Web.Services
  • System.Xml


3 - Abriremos el Archivo Main.cs con el editor de código y agregaremos las siguientes referencias de uso a los espacios de nombre que se detallan a continuación:

using System;
using Microsoft.Web.Services3;
using Microsoft.Web.Services3.Addressing;
using Microsoft.Web.Services3.Messaging;
using Microsoft.Web.Services3.Usuario;


4 - Como siguiente paso, eliminaremos el espacio de nombre actual con todo lo creado y agregaremos el siguiente espacio de nombre:

namespace UsuarioClienteLlamada
{

}

5 - Dentro del espacio de nombre creado agregaremos la siguiente clase:

class UsuarioClienteLlamada : AppBase
{

}

6 - Dentro del espacio de clase, agregaremos el siguiente método de lectura

[MTAThread]
static void Main(string[] args) {
UsuarioClienteLlamada ucl = null;

try
{
ucl = new UsuarioClienteLlamada();

}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}

Console.WriteLine("");
Console.WriteLine("Presione [Enter] para continuar...");
Console.WriteLine("");
Console.ReadLine();
}

7 - Antes de continuar con la definición de código de esta clase, agregaremos a la solución una nueva clase con el siguiente nombre:

UsuarioCliente.cs

8 - Abriremos el Archivo UsuarioCliente.cs con el editor de código y agregaremos las siguientes referencias de uso a los espacios de nombre que se detallan a continuación:

using System;
using Microsoft.Web.Services3;
using Microsoft.Web.Services3.Messaging;
using Microsoft.Web.Services3.Usuario;

9 - A continuación definiremos el siguiente espacio de nombre:

namespace UsuarioClienteLlamada
{

}

10 - Dentro del espacio de nombre creado, definiremos la siguiente clase:

[SoapService("http://localhost/Ejercicio002")]
public class UsuarioCliente : SoapClient
{
public UsuarioCliente(Uri to) : base(to) { }

}


NOTA. Se debe observar que en la clase existe un constructor de clase que captura la URL que se entrega, esta URL estará asociada al WebService que se desea consumir.

IMPORTANTE. Se debe considerar que los mismos espacios de nombre de cabecera SOAP deben ser definidos en los métodos que invocarán a la clase desde el cliente

11 - El siguiente paso, será crear el método que llamará al método de la clase creada en el Web Service, esto se hará a continuación del constructor definido, definiendo las siguientes líneas de código:

[SoapMethod("http://localhost/Ejercicio002/UsuarioRequest")]
public Usuarios funUsuarioConsultar(UsuarioRequest message)
{
// se invoca el servicio utilizando mensajería de punto a punto
// (SoapServiceAttribute.TargetNamespace)
// y se define la función que se consumirá y que retorna las
// listas con los valores rescatados
return (Usuarios)base.SendRequestResponse("funUsuarioConsultar",
message).GetBodyObject(typeof(Usuarios),
SoapServiceAttribute.TargetNamespace);
}



IMPORTANTE. Se debe considerar que los mismos espacios de nombre de cabecera SOAP deben ser definidos en los métodos que invocarán a la clase desde el cliente

Guardamos el código creado y regresamos al archivo Main.cs

12 - Dentro del espacio de la clase UsuarioClienteLlamada, agregaremos el siguiente procedimiento que se encargará de realizar el consumo de la clase antes creada y de retornar la información:

public void prcConsumoEjecutar()
{

String[] symbols = { "m.roa", "w.roa" };
UsuarioRequest request = new UsuarioRequest();
request.Usuarios = symbols;

UsuarioCliente proxy = new UsuarioCliente(new Uri("http://" + System.Net.Dns.GetHostName() + "/Wse/StockService.ashx"));

Console.WriteLine("Llamando {0}", proxy.Destination.Address.Value);

Usuarios quotes = proxy.funUsuarioConsultar(request);

Console.WriteLine("La llamada al Web Service ha sido exitosa:");

foreach (Usuario quote in quotes.usuarios)
{
Console.WriteLine("");
Console.WriteLine("Usuario:\t\t" + quote.usuario);
Console.WriteLine("Nombre:\t\t\t" + quote.Nombre);
Console.WriteLine("Apellido:\t\t" + quote.Apellido);
Console.WriteLine("Sueldo:\t\t\t" + quote.Sueldo.ToString());
}
}

13 - Por ultimo dentro del bloque try definido en el Método Main de la aplicación agregaremos las siguientes líneas de código:

ucl = new UsuarioClienteLlamada();

ucl.prcConsumoEjecutar();



Quedando el bloque try de la siguiente forma:


try
{
ucl = new UsuarioClienteLlamada();
ucl.prcConsumoEjecutar();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}

Bueno amigos tienen todo, ahora si no les funciona es porque han picado algo mal.

Manejo de errores


Bueno amigos, el manejo de errores es más que el utilizar el conocido try catch, aunque este es muy bueno para elevar errores que se producen en sub capas, cuando se están utilizando, ya que como sabemos, si trabajamos con aplicaciones portables, debemos manejar y tratar los errores en la interfaz de usuario, así cada desarrollador definirá que errores y que mensajes aplicar cuando se presenta un error.

El siguiente artículo está desglosado en dos partes:

  • Manejo de errores en capas con try catch.
  • Manejo de errores en interfaz y en general.


1 - Manejo de errores en capas con try catch:



La forma más sencilla de tratar y elevar un error desde una capa a otra hasta llegar a la interfaz de usuario es lanzando la excepción, esto lo podemos apreciar a continuación:

try{

}Catch (Exception ex){
throw ex;
}

Yo, cuando defino, ya sea una capa de datos y/o una capa de negocios, nunca trato los errores en estos niveles, ya que si no existe un buen mecanismo de control de errores en estas capas, estos se pierden. Además que al construír aplicaciones portables y reutilizables y, no estar haciendo un buen control de errores en ellas, en la interfaz de usuario se puede perder el error real y el desarrollador puede pasar algunos problemas al tratar de saber cual es el error verdadero. Por lo cual, recomiendo siempre que eleven los errores de las capas capturados en los bloques try catch y, realizar el tratamiento de errores en la interfaz de usuario, en adelante UI.

2 - Manejo de errores en interfaz y en general.


Ahora, es posible manejar y administrar los errores que se presentan en cada una de las páginas que se ejecutan en una aplicación web. Los diversos tipos de errores que se pueden administrar son los siguientes:

  • Capturar errores en una página.
  • Capturar Errores de Aplicación.
  • Manejando los errores en el web.config


Captura de errores en una página:

Cuando estamos en una interfaz, podemos ya manejar los errores con un solo procedimiento, que capturará todos los errores que se presenten en la página, claro esta que podríamos hacerlo con el bloque try catch, pero si podemos crear un método de página que puede controlar los errores que se lanzan desde capas inferiores o que puede capturar los errores que se procuden en la misma página, ¿¿¿para que definir por cada método de página un bloque try catch???. La forma de realizar este ejemplo lo veremos a continuación:


a) Crearemos una página con el nombre wfrError.aspx, dentro de una aplicación o sitio web en vs2003, 2005 y/o 2008.

b) En el método que atiende el evento load del formulario, lanzaremos un evento que desplegará un error, utilizando las siguientes líneas de código:

throw new ApplicationException("Manejo de Error de Interfaz");

Si ejecutamos la página dentro de la aplicación creada veremos que se despliega un error manejado por el IIS, ahora lo que debemos hacer es crear el método de página que controlará todos los errores que se lancen dentro de esta, para ello agregaremos el siguiente procedimiento, el cual estará encargado de capturar y administrar el error que se ha desplegado:

void Page_Error(Object sender, EventArgs args) {

Exception e = Server.GetLastError();

Response.Write("Lo sentimos, se ha producido el siguiente error" + e.ToString());
Server.ClearError();
}


Capturar Errores de Aplicación.

Si no es posible capturar un error en la página, se puede capturar este globalmente creando funciones en el archivo global.asax, específicamente en el método Application_Error.

En este ejemplo defino como crear un archivo de logs, pero yo ya no uso este tipo de manejo de errores debido a que las aplicaciones deben tener permiso de lectura, escritura y modificación, dentro del directorio en donde se escribe el archivo. Yo ahora guardo los log de errores en una tabla que creo en una Base de datos, la cual se puede gestionar de mejor forma si se monta una interfaz de control de errores y de avisos automáticos. El caso es que para este ejemplo se debe hacer lo siguiente:

a) Agregar un nuevo elemento a la solución, seleccionando una aplicación de clase de plantilla global, que lleva por nombre global.asax..

b) En el archivo global.asax agregar las siguientes líneas de código en el método Applicatio_Error:

void Application_Error(object sender, EventArgs e) {
try {
string strFile = Server.MapPath("log.txt");
System.IO.StreamWriter log = new System.IO.StreamWriter(strFile);

System.Exception exc = Server.GetLastError();

log.WriteLine(exc.ToString());
log.Close();
} catch (System.Exception ex) {

}
finally {
Response.Redirect("Pagina de mensaje de error amigable.aspx");
}
}

Ahora, si la página se salta el manejo de errores de página, el error se capturará en este método y se registrará, luego se redireccionará a una página que puede desplegar un mensaje amigable a un usuario.


Manejando los errores en el web.config

Bueno amigos, este tipo de majeo de errores es el más conocido y es el que se utiliza siempre, lo voy a poner solo por los muchachos que consultan mi blogs y que se están iniciando en esto.

El manejo de errores en el web.config, consiste más que en registrar un error, esta asociado a controlar posibles tipos de errores y/o desplegar una interfaz con un mensaje amigable para el usuario, la forma de definir este tipo de manejo de errores es la siguiente:


a) Dentro del nodo system.web del archivo web,config habilitaremos o agregaremos el subnodo customError.

b) En el nodo customError definiremos los siguientes parámetros:

<configuration>
<system.web>
<customErrors mode="RemoteOnly" defaultRedirect="pagina error amigable.aspx"/>
</system.web>
</configuration>


Con esto se logra que cuando exista un error en alguna aplicación se despliegue un mensaje amigable al usuario

c) Tambien es factible definir la redirección de páginas por un un número de error en especícico, por ejemplo:

<configuration>
<system.web>
<customErrors mode="RemoteOnly" defaultRedirect="frmMensaje.aspx>
<error statusCode="500" redirect="PaginaError.aspx"/>
</system.web>
</configuration>


Para probar el funcionamiento del mensaje de error, agregaremos en la página de Default.aspx un link button, apuntando a una página inexistente, al ejecutarse este link button al no existir la página se redireccionará a la página de error en donde se encuentra el mensaje respectivo.



martes, 1 de abril de 2008

Ds - Dt vs2005/8 - Introduccion

DataSet/DataTable tipados y Table Adapter con Visual Studio 2005 y 2008
Introducción- Data Label- Capa de Datos- Capa de Negocios- UI Interfaz


Bueno amigos.


Lo prometido es deuda, si es que vienen siguiendo los artículos que he publicado se darán cuenta que había prometido mostrarles como crear una aplicación en tres capas con el objeto dataset tipado de visual studio 2005, presente también en la nueva versión 2008 de este producto. Así que, esta forma de trabajar les permitirá utilizar esta filosofía en las dos versiones del producto de visual studio.




Antes de partir con los ejemplos, quisiera introducirlos a las modificaciones y mejoras que se han llevado a cabo en la versión de dataset tipados desde vs2003 a vs2005, que en mi opinion es enorme.


Si nos gusta trabajar orientado a los objetos, podemos modelar las entidades que pertenecen a un objeto en un dataset tipado correspondiente, el cual crea automáticamente las relaciones de referencia que existen entre estas entidades, mapenado las estructuras desde la misma base de datos (esto dependerá del proveedor de conexión que estemos utilizando) y las exije al igual que en un modelo entidad relación, lo cual no existía en vs2003, ya que las relaciones las debíamos crear nosotros. Para Sql Server, los mapeos y generaciones de estructuras a raíz de las entidades seleccionadas es completo.

En segundo caso han agregado un nuevo metodo llamado getData(), a parte del método Fill() asociado al adapter seleccionado dependiendo del proveedor.


Si recordamos, el método Fill, nos permitía cargar un DataTable dentro de un dataset, utilizando el objeto Adapter correspondiente al proveedor que estabamos utilizando.
  • En vs2005 se mantiene el método Fill en la zona de diseño de un table adapter, asociado a un datatable, ya que el adaptador de consultas se crea directamente cuando tipamos la entidad, con lo cual accedemos al objeto Fill desde el objeto dataset, es decir dataset.modelodetabla.Fill, etc.
  • A la plantilla de dataset tipados se le ha agregado una nueva opción denominada GETDATA, la cual permite crear desde la sección de table adapter consultas a bases de datos, pudiendo ser de sql y/o procedimientos almecenados. Los mapeos a procedimientos almacenados pueden ser definidos desde procedimientos existentes o crear estos desde el diseñador provisto por esta plantilla.

Estos ejemplos quedarán bien destacados en nuestra aplicación ya que trabajaremos con procedimientos almacenados.



Al crear una Biblioteca de clases utilizando estos objetos, veremos que cuando generamos la Capa de Datos, en el cuadro de herramientas disponibles se agregarán una serie de componentes resultado de él o los dataset tipados que definamos, es decir, si en un dataset tipado definimos la estructura de un objeto, todas las entidades que atienden a este objeto, sean una o muchas se generan dentro del DataSet, por lo cual observarán el nombre del dataset que han creado como componente, adicionalmente observarán todos los tableAdapter que se asocian a cada una de las entidades, es decir, verán un componente con el nombre de la entidad seguido de TableAdapter. Estos componentes generados tendrán todos los métodos GetData que deben realizar las transacciones con las entidades físicas.



Se torna muy importante destacar que cada vez que se realiza una modificación en el modelo de datos Entidad Relación, se deben realizar las modificaciones pertinentes en la capa de datos, así como en los métodos correspondientes de las diferentes capaz que lo acompañan.



Bueno ahora a lo suyo, ver segunda parte

  • En este artículo definiremos el modelo entidad relación en una Base de Datos Sql server 2000 o 2005, con procedimientos almacenados para mostrarles las opciones respectivas.

  • Crearemos una capa de datos desde una biblioteca de clases tratando de mostrar la forma de desarrollar orientado a objetos (Ya que en un ejemplo con dos entidades no queda muy claro este tema).

  • Crearemos una Capa de negocios que relizará las llamadas a los métodos creados en la Capa de Datos y que utilizará las estructuras definidas en ésta para retornar la información.

  • Por último, crearemos una UI (Interfaz de usuario que realizará las solicitudes y que utilizará las estructuras definidas para capturar la información). Claro está que para que las capaz de datos y negocios sean totalmente portables, solo deben recibir variables y parámetros, nunca componentes, ya que si definimos una aplicación web y pasamos a una capa de negocios un control para que se carge, este metodo ya no nos servirá para una aplicación web (Los controles son distintos).

Ds - Dt vs2005 - Data Label

DataSet/DataTable tipados y Table Adapter con Visual Studio 2005 y 2008
Introducción- Data Label- Capa de Datos- Capa de Negocios- UI Interfaz

Crear Capa de Datos

En esta fase del artículo definiremos la estructura de nuestra Base de Datos que llamaremos LinQ (La Base de datos que utilizamos en el ejercicio de linq to sql con las mismas entidades, para los que la tienen), así como los procedimientos almacenados que utilizaremos.

1 - Tablas
2 - Procedimientos

Debo mencionarles, que como hago estos ejemplos en la marcha, es decir, a medida que escribo el artículo, es posible que realicemos modificaciones o correcciones de los mismos en el transcurso del ejercicio. Espero que no, ya que la filosofía de trabajar a tres capas esta asociada a la posibilidad de definir cada paso del proyecto una vez terminada una fase anterior, es decir, modelamos e implantamos la Base de Datos, las entidades y los procedimientos almacenados, por que ya tenemos una versión sólida de esta que puede ser implementada, por lo cual la definición de una arquitectura debe ser ascendente. Con esto quiero decir, que, por lo menos en mi caso prefiero definir primero el modelo de datos, luego los procedimientos y funciones y a medida que se van agregando funcionalidades se debe seguir con el mismo paso, el modelo de datos, los procedimientos, y luego la Capa de Datos y la Capa de Negocios.


Mi objetivo no es enceñarles a trabajar Orientado a los objetos, pero en mi artículo de LinQ to Sql de Marzo del 2008 hago una receña a que es OO.


El modelo de datos que debemos implementar es muy simple, solo trabajaremos con 2 tablas y en la capa de datos, a pesar de definir un objeto con las dos entidades, solo crearemos los métodos de transacción de datos en una tabla. Bueno el modelo se observa a continuación:


Y como me gusta facilitarles las cosas les pongo los script de creación de los mismos. Para los que han realizado el ejemplo de Linq to Sql podrán apreciar que el modelo es el mismo.


TABLAS


CREATE TABLE [lnqEmpleado](
[empCodigo] [int] IDENTITY(1,1) NOT NULL,
[empNombre] [varchar](70) NOT NULL,
[empApellidoPrimero] [varchar](70) NOT NULL,
[empApellidoSegundo] [varchar](70) NOT NULL,
[empFechaActivacion] [datetime] NOT NULL,
[empEstado] [char](1) NOT NULL,
CONSTRAINT [PK_lnqEmpleado] PRIMARY KEY CLUSTERED
(
[empCodigo] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

CREATE TABLE [lnqUsuario](
[usuCodigo] [int] IDENTITY(1,1) NOT NULL,
[empCodigo] [int] NOT NULL,
[usuLogin] [char](10) NOT NULL,
[usuClave] [char](10) NOT NULL,
[usuEstado] [char](1) NOT NULL,
CONSTRAINT [PK_lnqUsuario] PRIMARY KEY CLUSTERED
(
[usuCodigo] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

ALTER TABLE [lnqUsuario] WITH CHECK ADD CONSTRAINT [FK_lnqUsuario_lnqEmpleado] FOREIGN KEY([empCodigo])
REFERENCES [lnqEmpleado] ([empCodigo])
ALTER TABLE [lnqUsuario] CHECK CONSTRAINT [FK_lnqUsuario_lnqEmpleado]

Los procedimientos almacenados que utilizaremos solo realizarán transacciones con la tabla lnqUsuario, estos procedimientos almacenados se los pongo a continuación (Miren que me gusta facilitar las cosas):


PROCEDIMIENTOS

CREATE PROCEDURE [dbo].[prcUsuarioConsultar]
@empCodigo int
AS
begin
select
lnqEmpleado.*
from
lnqEmpleado
where
lnqEmpleado.empCodigo = @empCodigo
end


CREATE PROCEDURE [prcUsuarioLista]
AS
begin
select
*
from
lnqUsuario
end

CREATE PROCEDURE [prcUsuarioInsertar]
@empCodigo int,
@usuLogin char(10),
@usuClave char(10),
@usuEstado char(1)
AS
begin
declare @iExiste int
set @iExiste = 0
select @iExiste = count(usuLogin) from lnqUsuario
where usuLogin = @usuLogin
if @iExiste>0
begin
return 1
end
else
begin
insert into lnqUsuario
(empCodigo, usuLogin, usuClave, usuEstado)
values
(@empCodigo, @usuLogin, @usuClave, @usuEstado)
end
return 0
end


CREATE PROCEDURE [prcUsuarioModificar]
@usuLogin char(10),
@usuClave char(10),
@usuEstado char(1)
AS
begin
declare @iExiste int
set @iExiste = 0
select @iExiste = count(usuLogin) from lnqUsuario
where usuLogin = @usuLogin
if @iExiste>0
begin
update lnqUsuario
set
usuClave = @usuClave,
usuEstado = @usuEstado
where
usuLogin = @usuLogin and
usuClave = @usuClave
end
return 0
end

CREATE PROCEDURE [prcUsuarioEliminar]
@usuLogin char(10),
@usuClave char(10)
AS
begin
declare @iExiste int
set @iExiste = 0
select @iExiste = count(usuLogin) from lnqUsuario
where usuLogin = @usuLogin
if @iExiste>0
begin
update lnqUsuario
set
usuEstado = 'I'
where
usuLogin = @usuLogin and
usuClave = @usuClave
end
return 0
end

Ds - Dt vs2005 - Capa Dato

DataSet/DataTable tipados y Table Adapter con Visual Studio 2005 y 2008
Introducción- Data Label- Capa de Datos- Capa de Negocios- UI Interfaz


Bueno amigos, la creación de la capa de datos en esta aplicación es muy simple, ya que como tenemos todo montado en la Base de Datos, que para mi opinion, crear las consultas y transacciones en la base de datos es mucho mejor que crear las consultas en la solución, es decir, en esta capa, es mucho mejor.

Por que digo esto, por la sencilla razón de que cuando definimos las consultas y transacciones en procedimientos almacenados y funciones, tenemos una precomplicación de las mismas y lo único que le queda al motor de base de datos, es ejecutar estas sin tener que hacer un analisis, lexico, sintaxico y semantico (lo explican en la uni cuando pasan sql (es lo básico)). Esto no quita que en algunas ocaciones, por motivos de portabilidad de las aplicaciones entre distintos motores de bases de datos, se opte por hacerlo en la capa de datos correspondiente, pero esto está amarrado a la posibilidad de distintos motores. En el caso de que trabajemos con un solo motor de base de datos, saquemosle el máximo de provecho a las herramientas y pasemos las cargas donde corresponden. Los pasos a seguir para crear nuestra Capa de Datos va a ser la siguiente:



Primero Crearemos un nuevo proyecto de vs2005 o vs2008, para los dos casos los pasos son los mismos. ir a Archivo/Nuevo/Proyecto y seleccionar una aplicación de windows que en este caso será una Biblioteca de Clases, como se muestra en las 2 siguientes imágenes.





Definiremos como nombre del proyecto CapaDato y como nombre de la solución WinApp. A continuación, borraremos la clase que se crea por defecto en el proyecto de bibliotecas de clases y seleccionaremos con el botón derecho del ratón, sobre el nombre del proyecto la opción Agregar/ Nuevo item, en donde se desplegará la ventana que presentará la opción de selección. En esta ventana seleccionaremos la plantilla DataSet, a la cual llamaremos dsUsuario.


Llevado a cabo este paso, ahora el siguiente paso será tipar el dataset, para ello, desde el explorador de servidores, crearemos una conexión al motor de bases de datos y a la Base de Datos en específico que deseamos utilizar para mapear. Abriremos el nodo de conexión y seleccionaremos, en este caso las tablas que deseamos utilizar, arrastrándolas al diseñador definido. Al terminar la acción el DataSet quedará de la siguiente forma:


Si observamos, podremos ver que las estructuras de entidades mapeadas han creado los campos y han creado por defecto un método Fill y otro de tipo GetData. El método Fill por defecto cargará la información de una consulta a un DataTable perteneciente a un dataset que contenga la estructura definida y, el método GetData realizará lo mismo, pero solo retornará un DataTable con el resultado. Estos métodos si se ejecutan retornan toda la información que contiene la tabla en la Base de Datos.


Si se observa, estos métodos están definidos en una sección denominada TableAdapter, por lo cual, no tenemos que crear un Adapter para cargar la información a un DataSet o a un DataTable como lo haciamos en 2003, todo a quedado mapeado y el diseñador nos ha hecho estos métodos por defecto. Ver siguiente figura.

El siguiente paso será asociar nuestros procedimientos a nuestro TableAdapter, para ello deberemos crear unas nuevas consultas de transacciones, para ello nos posicionaremos sobre el table adapter y seleccionaremos con el botón derecho del mouse la opción agregar consulta, como se muestra en la siguiente imagen.


Esto nos habrirá la herramienta de diseño que nos permitirá definir que deseamos hacer.

  • La primera nos permite crear consultas sql que estarán en nuestra capa de datos, estas consultas pueden ser de Insert, Update, Delete y de consultas.
  • La segunda opción nos permite crear procedimientos almacenados en nuestra Base de Datos, directamente.
  • La tercera opción, nos permite consumir los procedimientos almacenados que hemos construído con anterioridad. Un buen diseñador de arquitectura trabaja de esta forma

Ver figura


Al seleccionar la opción de usar procedimientos almacenados , nos desplegará una lista con todos los procedimientos almacendados disponibles en la base de datos, como se muestra en la siguiente figura.




Para realizar el ejemplo, seleccionaremos el procedimiento prcUsuarioListar, el cual al ser seleccionado, desplegará en pantalla los datos que solicita para ser ejecutado correctamente. Al seleccionar la opción siguiente prodremos observar que se despliegan tres opciones.

  • Desplegar la información en filas y columnas.
  • Entregar solo un resultado
  • No retornar valor

Cuando ejecutamos una consulta que retorna una select en un procedimiento almacenado deberemos seleccionar la primera opción, en el caso de que solo deseemos ejecutar un procedimiento que haga algo y debamos controlar un retorno de datos, es decir, 0 o 1 como en nuestros procedimientos de actualización, modificación y eliminación, seleccionaremos la segunda opción. Para el procedimiento seleccionado ejecutaremos la primera opción.


El siguiente paso es definir los métodos Fill y GetData que utilizaremos para la carga de datos de un DataTable dentro de un DataSet o un DataTable, si vamos a retornar solo las filas, solo debemos utilizar un DataTable, para ello solo definiremos que solo utilizaremos DataTable.


Para nuestro ejemplo, solo utilizaremos DataTables y verán que con esta filosofía casi siempre se usa solo la segunda opción, ya que las modificaciones se realizan directamente a la Base de Datos y no se utiliza un DataSet de por medio para modificar y ejecutar el metodo Update del mismo.




Al generar el nuevo método veremos que la sección de Table Adapter cuenta con dos nuevos métodos que retornarán la misma información de dos formas de llamada y carga distintos DataSet y DataTable o DataTable solamente.



Los procedimientos de inserción, modificación y eliminación, retornarán un solo valor 0 o 1, por lo cual no se crean métodos Fill y/o GetData, esto se puede observar en la siguiente figura.



El ultimo paso y el más importantes es el de generación, con el cual se generarán los controles que podremos utilizar desde el cuadro de herramientas cuando creemos los objetos desde la Capa de Negocios.



Bueno como ven, cuando una plicación se encuentra bien diseñada podremos trabajar muy rapidamente en esta capa.




No dejen de lado el trabajar Orientado a Objetos, les facilita la programación, pero la programación OO depende del Análisis y Diseño. Si el análisis y Diseño no esta orientado a Objetos, aunque sean unos muy buenos programadores, nunca podrán desarrollar un diseño que no esta orientado a objetos, esto dará como resultado algo a "medio morir saltando" (chilenismo).

Ds - Dt vs2005 - Capa Negocio

DataSet/DataTable tipados y Table Adapter con Visual Studio 2005 y 2008
Introducción- Data Label- Capa de Datos- Capa de Negocios- UI Interfaz

Crear Capa de Negocios


Despues de haber creado la Capa de Datos y de generar ésta, lo siguientes es crear nuestra Capa de Negocios. En esta utilizaremos las estructuras definidas en nuestra capa de datos con el objeto de capturar la información y realizaremos las llamadas a los métodos de transacciones definidos que nos permitirán realizar desde la Capa de Datos el acceso a la Base de Datos. Lo ideal es que las llamadas a la Base de Datos siempre se hagan desde la Capa de Negios hacia la Capa de Datos y que la ultima realice la transacción y entregue el resultado a la Capa de Negocio. A su vez, la Capa de Negocio es instanciada desde la UI (Interfaz de Usuario) y esta a su vez, retorna los resultados a ésta.

Para poder construir una capa de negocio, se necesita crear un nuevo proyecto de bilbioteca de clases, el cual nos permitirá manejar todo en ambito del negocio dentro de esta clase. Adicionalmente, en esta Capa deben existir los objetos que se han definido en la Capa de datos, ya que los objetos son parte importante de un diseño OO y de una arquitectura de aplicación (A ver si algún día publico como hacer un buen análisis y diseño Orientado a los Objetos), ya que todos dicen siempre saber, pero nunca lo hacen. Para crear el nuevo proyecto, desde el explorador de soluciones seleccionaremos con el boton derecho del mouse (sobre la solución), la opcion agregar/nuevo proyecto, como se muestra a continuación:


Se deplegará la interfaz de definición de proyectos en donde definiremos el nombre de este, como se muestra a continuación:





Bueno, ya creado el proyecto de Biblioteca de Clases que hemos llamada CapaNegocio, borraremos la clase que se crea por defecto y, con el botón derecho de mouse, seleccionaremos (sobre el proyecto creado), la opción Agregar/nuevo item y seleccionaremos la plantilla de Clase de Componentes, que la llamaremos clsEmpleado, respetando el nombre del objeto, como se muestra a continuación:





Al agregar este elemento veremos que se despliegua un diseñador (Bueno, hasta acá todo bien). El siguiente paso será ir a la ventana de herramientas y veremos que al generar el proyecto de Capa de Datos, en el paso anterior se han agregado los controles asociados al dataset y a los TableAdapters que se encuentran definidos para cada una de las entidades mapeadas que componen el objeto, si no se encuentran los componentes del objeto CapaDato en el cuadro de herramientas, deben volver a generar este. Para poder verlos también, deben de tener habierto el diseñador del objeto de clase de componentes que se ha definido. Ver figura.


El siguiente paso será arrastrar a la ventana del diseñador el componente lnqUsuarioTableAdapter, en el diseñador, seleccionaremos el componente agregado y en la ventana de propiedades lo renombraremos como dallnqUsuario (dal de Data Access Label).

Lo que hemos hecho es definir la instancia de llamada al control de la capa de datos que contiene el o los métodos de transacción con la Base de Datos, la interfaz quedará de la siguiente forma:



Al inluir el componente al diseñador, automáticamente se agregar al proyecto de Capa de Negocio una referencia al proyecto de Capa de Datos y en este caso, tenemos acceso a los métodos del TableAdapter lnqUsuarioTableAdapter.


Hasta ahora todo bien, pero tenemos que picar código, para ello nos posicionaremos en el diseñador y seleccionaremos con el botón derecho del mouse la opción Ver código, el cual nos mostrará la ventana de código de la aplicación. Ver figura.



Esto abrirá el archivo de código de la clase de componentes, como yo trabajo en C#, espero que no les moleste que presente los códigos de de los métodos con este lenguaje.

Dentro del espacio de trabajo de la clase y debajo del contructor agregaremos la función que será llamada desde la UI y que retornará la lista de los Usuarios, la cual se muestra a continuación:


// funci¢n encargada de retornar la informacion
public CapaDato.dsEmpleado.lnqUsuarioDataTable fnUsuarioLista() {
CapaDato.dsEmpleado.lnqUsuarioDataTable dt = new
CapaDato.dsEmpleado.lnqUsuarioDataTable();
try{
dt = this.dallnqUsuario.GetDataByUsuarioListar();
}catch (Exception ex) {
throw ex;
}
return dt;
}

Analisemos el código


Como se observa, creamos una función fnUsuarioLista, que retornará la estructura de la tabla lnqUsuario con los datos que este tenga asociados, en el caso de que exista algún tipo de problema elevamos una exception a la UI, que es donde se deben definir los controles de errores para cada aplicación que consuma las llamadas.


Si se observa dallnqUsuario referencia al TableAdapter de la Capa de Datos y es esta la que realmente realiza la transacción con la Base de Datos.


A continuación pongo todos los otros códigos de transacción entre la Capa de Datos y la Capa de Negocios.

// funcion encargada de realizar la inserci¢n de datos de un nuevo usuario
public int prcUsuarioInsertar(Int32 empCodigo, string usuLogin, string usuClave) {
int iReturn = 0;
try {
this.dallnqUsuario.prcUsuarioInsertar(empCodigo, usuLogin, usuClave, ('A').ToString());
}catch (Exception ex) {
throw ex;
}
return iReturn;
}

// funcion encargada de modificar los datos de un usuario
public void prcUsuarioModificar(string usuLogin, string usuClave, string usuEstado){
try {
this.dallnqUsuario.prcUsuarioModificar(usuLogin, usuClave, usuEstado);
}
catch (Exception ex)
{
throw ex;
}
}


// funci¢n encargada de eliminar los datos de usuario
public void prcUsuarioEliminar(string usuLogin, string usuClave) {
try{
this.dallnqUsuario.prcUsuarioEliminar(usuLogin, usuClave);
}catch (Exception ex){
throw ex;
}
}


Lo que nos queda es definir lo mismo que hicimos para lnqUsuariosTableAdapters en lnqEmpleadosTableAdapters, es decir, arrastrar este componente desde el cuadro de herramientas al diseñador, renombrarlos como dallnqEmpleado y luego crear la función de consulta del procedimiento utilizando el id seleccionado, todo quedaría de la siguiente forma:

Y la función que retorna el registro debe quedar de la siguiente forma:


// funci¢n que retorna la informaci¢n de los datos del empleado
public CapaDato.dsEmpleado.lnqEmpleadoDataTable fnUsuarioConsultar(int empCodigo) {
CapaDato.dsEmpleado.lnqEmpleadoDataTable dt = new
CapaDato.dsEmpleado.lnqEmpleadoDataTable();
try {
dt = this.dallnqEmpleado.GetDataByEmpleadoConsultar(empCodigo)
}catch (Exception ex){
throw ex;
}
return dt;
}


Bueno amigos, con esto hemos dado una pincelada a los conceptos de programación orientada a los objetos en vs2005 y vs2008, ya nos queda poco, solo falta la UI.