10. La aplicación [SimuPaie] —versión 6—: un cliente ASP.NET de un servicio web
10.1. La arquitectura de la aplicación
Estamos desarrollando la arquitectura cliente/servidor de la prueba NUnit de la siguiente manera:
![]() |
En [1], la prueba NUnit se sustituye por la aplicación web de la versión 8. Es importante señalar aquí que esta arquitectura incluye dos servidores web que no se muestran:
- un servidor web que ejecuta el servicio web [S]. Se ejecutará en una primera instancia de Visual Web Developer.
- un servidor web que ejecuta el cliente web [1]. Se ejecutará en una segunda instancia de Visual Web Developer.
10.2. El proyecto de Visual Web Developer para el cliente [web]
Creamos un nuevo proyecto web ASP.NET:
![]() |
- en [1], seleccionamos un proyecto web en C#
- En [2], seleccionamos «Aplicación web ASP.NET»
- en [3], le damos un nombre al proyecto web
- en [4], especificamos una ubicación para este proyecto
- en [5], se crea el proyecto
Modificamos algunas propiedades del proyecto:
![]() |
- en [1], el nombre del ensamblado que se generará
- En [2], el espacio de nombres predeterminado para las clases e interfaces que se crearán
Para compilar el proyecto [pam-v6-client-webservice], podemos recuperar los archivos [Global.asax] y [Default.aspx] del proyecto web [pam-v4-3tier-nhibernate-multivues-monopage] y copiarlos en el proyecto [pam-v6-client-webservice]. Para ello, primero utilizamos el Explorador de Windows:
![]() |
- en [1], la carpeta del proyecto [pam-v4-3tier-nhibernate-multivues-monopage]
- en [2], la carpeta del proyecto [pam-v6-client-webservice] tras copiar
- las carpetas [images, pam, resources]
- los archivos [Global.asax, Global.asax.cs]
- los archivos [Default.aspx, Default.aspx.cs, Default.aspx.designer.cs]
- en [3], en Visual Studio Express, muestra todos los archivos del proyecto para ver los archivos recién añadidos
- En [4], incluye las carpetas y los archivos añadidos en el proyecto
![]() |
- en [5], el nuevo proyecto [pam-v6-client-webservice]
En este punto, puedes compilar el proyecto por primera vez [6]. Aparecerán los siguientes errores:
Para comprender y solucionar estos errores, debemos analizar la arquitectura del proyecto web en desarrollo:
![]() |
El proyecto [pam-v6-client-webservice] es la capa [web] [1] del diagrama anterior. Podemos ver que esta capa se comunica con el cliente del servicio web [C], un cliente que generaremos en breve.
- Los errores 2, 5 y 7 se deben a que el código de [pam-v4] hacía referencia a la DLL de la capa [business], una DLL que ahora se encuentra en el lado del servidor en lugar de en el lado del cliente.
- Los errores 1 y 3 tienen la misma causa, pero en este caso se refieren a la DLL de la capa [DAO].
- El error 4 se debe a que el código de [Global.asax] utiliza Spring, y nuestro proyecto no hace referencia a la DLL de Spring. Añadiremos esta referencia.
- El error 6 se debe a que la clase [Employee] está definida en la capa [DAO], que no existe en el lado del cliente.
Los errores de espacio de nombres 1, 2, 4, 5, 6 y 7 se resolverán generando el cliente C para el servicio web. El error 3 se resuelve añadiendo una referencia a la DLL de Spring en el proyecto. Podemos proceder de la siguiente manera:
![]() |
- En [1], añade una referencia al proyecto [pam-v6-client-webservice]
- En [4], hemos añadido una referencia a la DLL [Spring.Core] en la carpeta [lib].
Tras añadir esta referencia a Spring, la generación del proyecto muestra un error menos. El resto de errores se deben a líneas de código que hacen referencia a objetos de las capas [business] y [DAO], que ahora se encuentran en el servidor. Veremos que estos objetos que faltan se generarán en el cliente del servicio web [C].
![]() |
Antes de generar el cliente del servicio web [C], cambiaremos el espacio de nombres de las distintas clases presentes. Actualmente se encuentran en el espacio de nombres [pam-v4]. Cambiamos este espacio de nombres a [pam-v6]. Los archivos afectados son los siguientes: Default.aspx.cs, Default.aspx.designer.cs, Global.asax.cs. Por ejemplo:
....
namespace pam_v6
{
public class Global : System.Web.HttpApplication
{
// --- static application data ---
public static Employe[] Employes;
public static IPamMetier PamMetier = null;
....
Línea 2: El espacio de nombres pam_v4 ha sido sustituido por el espacio de nombres pam_v6.
Además, debe modificar el marcado en los siguientes archivos: Default.aspx y Global.asax:
![]() |
El código de [Default.aspx] queda así:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="pam_v6.PagePam" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
.....
Línea 1: El atributo Inherits especifica el nombre de clase del archivo [Default.aspx.cs]. Cambiamos el espacio de nombres.
El marcado de [Global.asax] queda así:
<%@ Application Codebehind="Global.asax.cs" Inherits="pam_v6.Global" Language="C#" %>
En el ejemplo anterior, cambiamos el espacio de nombres del atributo Inherits.
Ahora generamos el cliente del servicio web [C].
![]() |
Para realizar los siguientes pasos, el servicio web [S] [pam-v5-webservice] debe estar en ejecución en otra instancia de Visual Web Developer.
![]() |
- En [1], añadimos una referencia al servicio web [pam-v5-webservice] al proyecto [pam-v6-client-webservice]
- En [2], introduzca el URI del servicio web [pam-v5-webservice]. Esta es la URL del archivo WSDL de este servicio web. Al compilar el servicio web [pam-v5-webservice], indicamos cómo encontrar esta URL.
- En [3], indique al asistente que utilice el archivo WSDL especificado en [2]
- en [4], el servicio web detectado [Service1Soap] y en [5] los métodos remotos que expone.
- En [6], establecemos el espacio de nombres en el que se generarán las clases e interfaces del cliente [C]
- Comenzamos a generar el cliente [C]
![]() |
- En [1], el cliente generado. Haga doble clic en él para acceder a su contenido.
- En [2], en el Explorador de objetos, se muestran las clases e interfaces del espacio de nombres pam_v6.WsPam. Este es el espacio de nombres del cliente generado.
- [3] muestra la clase que implementa el cliente del servicio web.
- En [4], los métodos implementados por el cliente [Service1SoapClient]. Aquí encontramos los dos métodos del servicio web remoto [5] y [6].
- En [2] se muestran los diagramas de entidades de las capas:
- [negocio] : Nómina, PartidasDeNómina
- [DAO]: Empleado, Cotizaciones, Complementos
De cara al futuro, ten en cuenta que estas representaciones de las entidades remotas se encuentran en el lado del cliente y en el espacio de nombres pam_v6.WsPam.
Examinemos los métodos y propiedades que expone una de ellas:
![]() |
- En [1], seleccionamos la clase local [Employee]
- En [2], vemos las propiedades de la entidad remota [Employee], así como los campos privados utilizados para las necesidades específicas de la entidad local.
Examinemos el código de [Global.asax.cs] que contiene errores:
![]() |
- Las líneas 3 y 4 utilizan espacios de nombres que existen en el servidor, pero no en el cliente.
- La línea 13 utiliza una clase [Employee] para la que no se ha declarado el espacio de nombres correcto
- La línea 14 utiliza una interfaz IPamMetier que el cliente desconoce.
Ya nos hemos encontrado con problemas similares en el cliente C# mencionado anteriormente.
En la arquitectura:
![]() |
- la capa [de negocio] local está implementada por el cliente [C] de tipo pam_v6.WsPam.Service1SoapClient, que no implementa la interfaz IPamMetier aunque tenga métodos con los mismos nombres.
- Las entidades gestionadas (Employee, Benefits, Contributions) por el cliente [C] generado se encuentran en el espacio de nombres pam_v6.WsPam
El código de [Global.asax.cs] cambia de la siguiente manera:
using System;
using System.Collections.Generic;
using Pam.Web;
using Spring.Context.Support;
using pam_v6.WsPam;
namespace pam_v6
{
public class Global : System.Web.HttpApplication
{
// --- static application data ---
public static Employe[] Employes;
public static Service1SoapClient PamMetier = null;
public static string Msg;
public static bool Erreur = false;
// application startup
public void Application_Start(object sender, EventArgs e)
{
// using the configuration file
try
{
// instantiation layer [metier]
PamMetier = ContextRegistry.GetContext().GetObject("pammetier") as Service1SoapClient;
// simplified list of employees
Employes = PamMetier.GetAllIdentitesEmployes();
// we succeeded
Msg = "Base chargée...";
}
catch (Exception ex)
{
// we note the error
Msg = string.Format("L'erreur suivante s'est produite lors de l'accès à la base de données : {0}", ex);
Erreur = true;
}
}
public void Session_Start(object sender, EventArgs e)
{
// put an empty simulation list in the session
List<Simulation> simulations = new List<Simulation>();
Session["simulations"] = simulations;
}
}
}
La línea 24 instancia la capa [C] utilizando Spring. La configuración necesaria para esta instanciación se define en [web.config]:
<configSections>
<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
...
</sectionGroup>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<object id="pammetier" type="pam_v6.WsPam.Service1SoapClient,pam-v6-client-webservice"/>
</objects>
</spring>
Línea 16: La capa [business] es una instancia de la clase [pam_v6.WsPam.Service1SoapClient], que se encuentra en el archivo DLL [pam-v6-client-webservice]. Recuerda que configuramos el proyecto [pam-v6-client-webservice] para generar este archivo DLL.
Todavía hay algunos errores en [Default.aspx.cs]:
![]() | ![]() |
- líneas 5 y 6: estos espacios de nombres ya no existen. Las entidades se encuentran ahora en el espacio de nombres pam_v6 o pam_v6.WsPam.
- línea 98: el cliente generado [C] no heredó la clase [PamException] de la capa [dao]. No pudo hacerlo, ya que el servicio web no expone esta excepción. Hemos optado por sustituir PamException por su clase padre, Exception.
El código queda así:
...
using System.Web.UI.WebControls;
using pam_v6.WsPam;
namespace pam_v6
{
public partial class PagePam : Page
{
...
...
try
{
feuillesalaire = Global.PamMetier.GetSalaire(DropDownListEmployes.SelectedValue, HeuresTravaillées, JoursTravaillés);
}
catch (Exception ex)
{
...
Una vez corregidos estos errores, podemos ejecutar la aplicación web:
![]() |
- en [1], la URL del cliente web para el servicio web remoto
- en [2], el menú desplegable de empleados se ha rellenado. Los datos que contiene proceden del servicio web.
Invitamos al lector a probar esta versión 6, una copia de la versión 4.

















