Skip to content

9. La aplicación [SimuPaie] – version 5 – ASP.NET / servicio web


Lecturas recomendadas: referencia [2], Introducción a C# 2008, capítulo 10 «Servicios web»


9.1. La nueva arquitectura de la aplicación

La arquitectura por capas de la aplicación Pam es actualmente la siguiente:

La vamos a desarrollar de la siguiente manera:

Mientras que en la arquitectura anterior, las capas [web], [metier] y [dao] se ejecutaban en una misma máquina virtual .NET, en la nueva arquitectura, la capa [web] se ejecutará en una máquina virtual diferente a las capas [metier] y [dao]. Este será el caso, en particular, si la capa [web] se encuentra en una máquina M1 y las capas [metier] y [dao] en una máquina M2. Aquí tenemos una arquitectura cliente/servidor:

  • el servidor está formado por las capas [metier] y [dao]. Al tratarse de un servicio web, necesita el servidor web n.º 2 para ejecutarse.
  • El cliente está formado por la capa [web]. Para ejecutarse, necesita el servidor web n.º 1.
  • El cliente y el servidor se comunican a través de la red Tcp / Ip con el protocolo HTTP / SOAP. Para ello, deben añadirse dos nuevas capas a la arquitectura:
    • la capa [S], que será un servicio web. El servicio web recibe las solicitudes de los clients remotos y utiliza las capas [metier] y [dao] para satisfacerlas. Hay muchas formas de construir un servicio Tcp / Ip. La ventaja del servicio web es doble:
      • utiliza el protocolo HTTP, que los cortafuegos de empresas y administraciones dejan pasar
      • utiliza un subprotocolo HTTP / SOAP estándar, implementado por numerosas plataformas de desarrollo: .Net, Java, Php, Flex, ... De este modo, un servicio web puede ser «consumido» (este es el término habitual) por clients, .Net, Java, Php, Flex, etc.
    • la capa [C], que será el cliente del servicio web remoto. Su función será comunicarse con el servicio web [S].

Esta nueva arquitectura se puede derivar de las anteriores sin demasiado esfuerzo:

  • las capas [metier] y [dao] permanecen sin cambios
  • la capa [web] evoluciona ligeramente, esencialmente para hacer referencia a entidades como Empleado, FeuilleSalaire, que se han convertido en entidades de la capa cliente [C]. Estas entidades son análogas a las de las capas [metier] o [dao], pero pertenecen a espacios de nombres diferentes.
  • La capa de servidor [S] es una clase que implementa la interfaz IPamMetier de la capa [metier]. Esta implementación se limita a invocar los métodos correspondientes de la capa [metier]. Los métodos implementados por la capa de servidor [S] quedarán «expuestos» a los clients remotos, que podrán invocarlos.
  • La capa de cliente [C] será generada por Visual Studio.

Los principios de la nueva arquitectura son los siguientes:

  • la capa [web] sigue comunicándose con la capa [metier] como si esta fuera local. Para ello, la capa cliente [C] implementa lainterfaz IPamMetier de la capa [metier] real y se presenta ante la capa [web] como una capa [metier] local. Aparte del problema de los espacios de nombres mencionado anteriormente, la capa [web] no cambia. Esa es la ventaja de haber trabajado por capas. Si se hubiera construido una aplicación de una sola capa, habría que rediseñarla en profundidad.
  • La capa cliente [C] transmite, de forma transparente para la capa [web], las solicitudes de esta última al servicio web remoto [S]. Se encarga de todo el aspecto de la «comunicación de red». Recibe una respuesta del servicio web remoto que formatea para entregarla a la capa [web] en el formato que esta espera.
  • En el lado del servidor, el servicio web [S] recibe comandos de sus clients remotos. Los formatea para llamar a los métodos de la interfaz IPamMetier de la capa [metier]. Cuando recibe la respuesta de la capa [metier], la formatea para transmitirla a través de la red al cliente [C]. Las capas [metier] y [dao] no necesitan modificarse.

9.2. El proyecto Visual Web Developer del servicio web

Creamos un nuevo proyecto con Visual Web Developer:

  • en [1], seleccionamos un proyecto web en C#
  • en [2], elegimos «Aplicación de servicio web ASP.NET»
  • en [3], le damos un nombre al proyecto web
  • en [4], indicamos una ubicación para este proyecto
  • en [1], el proyecto generado. Se trata de un proyecto web clásico con las siguientes características:
    • se ha indicado que el proyecto es de tipo «servicio web». Un servicio web no envía páginas web HTML a sus clients, sino datos en formato XML. Por lo tanto, la página [Default.aspx] que se genera habitualmente no se ha generado.
  • En [2], se ha generado un archivo [Service1.asmx] con el siguiente contenido:

<%@ WebService Language="C#" CodeBehind="Service1.asmx.cs" Class="pam_v5_webservice.Service1" %>
  • (continuación)
    • - la etiqueta WebService indica que [Service.asmx] es un servicio web
    • - el atributo CodeBehind indica la ubicación del código fuente de este servicio web
    • - el atributo Class indica el nombre de la clase que implementa el servicio web en el código fuente

El código fuente [Service.asmx.cs] del servicio web generado por defecto es el siguiente:


using System.Web.Services;

namespace pam_v5_webservice
{
  /// <summary>
  /// Descripción resumida de Servicio1
  /// </summary>
  [WebService(Namespace = "http://tempuri.org/")]
  [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
  [System.ComponentModel.ToolboxItem(false)]
  // Para permitir la llamada a este servicio web desde un script utilizando ASP.NET AJAX, elimine los símbolos de comentario de la siguiente línea. 
  // [System.Web.Script.Services.ScriptService]
  public class Service1 : System.Web.Services.WebService
  {

    [WebMethod]
    public string HelloWorld()
    {
      return "Hello World";
    }
  }
}
  • línea 8: la anotación WebService, que hace que la clase Service1 de la línea 13 se exponga como un servicio web. Un servicio web pertenece a un espacio de nombres para evitar que dos servicios web en todo el mundo tengan el mismo nombre. Más adelante tendremos que cambiar este espacio de nombres.
  • línea 13: la clase Service1 deriva de la clase WebService del marco .NET.
  • línea 16: la anotación WebMethod hace que el método así anotado se exponga a los clients remotos, que podrán llamarlo.
  • Líneas 17-20: el método HelloWorld es un método de demostración. Lo eliminaremos más adelante. Nos permite realizar las primeras pruebas y descubrir las herramientas de Visual Studio, así como algunos aspectos que conviene conocer sobre los servicios web.
  • En [1], ejecutamos el servicio web [Service.asmx]
  • VS Web Developer ha iniciado su servidor web integrado y lo ha configurado para que escuche en un puerto aleatorio, en este caso el 1599. A continuación, se ha solicitado al servidor web la página Url [2]. Se trata de una página de prueba del servicio web.
  • En [3], un enlace que permite visualizar el archivo de descripción del servicio web. Este archivo, denominado WSDL (WebService Description Language) debido a su extensión (.wsdl), es un archivo XML que describe los métodos expuestos por el servicio web. A partir de este archivo WSDL, los clients pueden conocer:
    • el espacio de nombres del servicio web
    • la lista de métodos expuestos por el servicio web
    • los parámetros esperados por cada uno de ellos
    • la respuesta devuelta por cada uno de ellos
  • en [4], el único método expuesto por el servicio web .
  • en [5], el contenido del archivo WSDL obtenido a través del enlace [3]. Cabe destacar el URL [6]. Es necesario conocerlo para el clients del servicio web.
  • en [7], la página obtenida al seguir el enlace [4] permite llamar al método [HelloWorld] del servicio web
  • en [8], el resultado obtenido: una respuesta XML. Cabe destacar el URL [9] del método.

El análisis de las páginas anteriores permite comprender cómo se invoca un método de un servicio web y qué tipo de respuesta devuelve. Esto permite escribir clients HTTP capaces de interactuar con el servicio web. La mayoría de los IDE actuales permiten la generación automática de este cliente HTTP, lo que evita al desarrollador tener que escribirlo. Este es el caso, en particular, de Visual Studio Express.

Antes de continuar con este proyecto, vamos a cambiar el espacio de nombres utilizado por defecto al generar las clases:

Cuando seleccionamos las propiedades del proyecto (clic derecho en el proyecto / Propiedades), tenemos en [1] el nombre del ensamblado del proyecto y en [2] su espacio de nombres por defecto.

Una vez hecho esto,

  • en [Service1.asmx.cs], cambiamos el espacio de nombres de la clase:

using System.Web.Services;

namespace pam_v5
{
...
  public class Service1 : System.Web.Services.WebService
  {
...
  }
}
  • en [Service.asmx], también cambiamos el espacio de nombres utilizado para la clase [Service1] (clic derecho / mostrar el marcado):

<%@ WebService Language="C#" CodeBehind="Service1.asmx.cs" Class="pam_v5.Service1" %>

Volvamos a la arquitectura de nuestra aplicación:

  • la capa [S] es el servicio web. Se limita a exponer los métodos de la capa [metier] a clients remotos. Esta es la capa que estamos construyendo.
  • La capa [C] es el cliente HTTP del servicio web. Esta es la capa que los IDE saben generar automáticamente.
  • La capa [web] ve la capa [C] como una capa [metier] local si nos aseguramos de que la capa [C] implemente lainterfaz de la capa [metier] remota.

A continuación vemos que nuestro servicio web va a:

  • exponer los métodos de la capa [metier]
  • interactuar con esta última, que a su vez interactuará con la capa [dao].

Por lo tanto, el proyecto debe utilizar los DLL de las capas [metier] y [dao]. Se desarrolla de la siguiente manera:

  • a [1], se añaden referencias al proyecto
  • en [2], se seleccionan las DLL habituales de la carpeta [lib]. Se debe asegurarse de que todas tengan la propiedad «Copia local» establecida en True. Los DLL seleccionados son aquellos que implementan las capas [metier] y [dao] con soporte NHibernate.

Una aplicación web de tipo «servicio web ASP.NET» puede tener una clase de aplicación global «Global.asax», al igual que una aplicación «sitio web ASP.NET» clásica. Ya hemos visto la ventaja de una clase de este tipo:

  • se instancia al iniciar la aplicación y permanece en memoria
  • , por lo que puede almacenar datos compartidos por todos los clients y que son de solo lectura. En nuestra aplicación, almacenará, al igual que en las anteriores, la lista simplificada de empleados. Esto evitará tener que buscar esta lista en la base de datos cuando un cliente la solicite.
  • en [1], hacer clic con el botón derecho del ratón sobre el proyecto
  • en [2], seleccione option [Ajouter un nouvel élément]
  • en [3], seleccione [Classe d'application globale]
  • en [4], se ha añadido el archivo [Global.asax] al proyecto

El contenido del archivo [Global.asax] es el siguiente:


<%@ Application Codebehind="Global.asax.cs" Inherits="pam_v5.Global" Language="C#" %>

El contenido del archivo [Global.asax.cs] es el siguiente:


using System;

namespace pam_v5
{
  public class Global : System.Web.HttpApplication
  {

    protected void Application_Start(object sender, EventArgs e)
    {

    }
...
  }
}

¿Qué debemos hacer en el método Application_Start? Exactamente lo mismo que en las aplicaciones web anteriores. Volvamos a la arquitectura de la aplicación y coloquemos allí la clase [Global]:

En el esquema anterior,

  • la clase [Global] se instancia al iniciar el servicio web. Permanece en memoria mientras el servicio web está activo.
  • La clase [Global] instancia las capas [metier] y [dao] en su método [Application_Start]
  • Para mejorar el rendimiento, la clase [Global] almacena la lista simplificada de empleados en un campo interno. A partir de este campo, generará la lista de empleados.
  • El servicio web se instancia con cada solicitud de un cliente. Desaparece tras haberla atendido. No se dirigirá directamente a la capa [metier], sino a la clase [Global]. Esta implementará la interfaz de la capa [metier].

La clase [Global] es análoga a la ya construida para las aplicaciones anteriores:


using System;
using Pam.Dao.Entites;
using Pam.Metier.Entites;
using Pam.Metier.Service;
using Spring.Context.Support;

namespace pam_v5
{
  public class Global : System.Web.HttpApplication
  {
    // --- datos estáticos de la aplicación ---
    public static Employe[] Employes;
    public static IPamMetier PamMetier = null;

    protected void Application_Start(object sender, EventArgs e)
    {
      // instanciación de la capa [metier]
      PamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
      // se recupera la tabla simplificada de empleados 
      Employes = PamMetier.GetAllIdentitesEmployes();
    }

    // lista simplificada de empleados
    static public Employe[] GetAllIdentitesEmployes()
    {
      return Employes;
    }

    // salario de un empleado
    static public FeuilleSalaire GetSalaire(string SS, double heuresTravaillées, int joursTravailles)
    {
      return PamMetier.GetSalaire(SS, heuresTravaillées, joursTravailles);
    }
  }
}

La clase [Global] implementa la interfaz [IPamMetier], pero esto no se indica explícitamente en la declaración:


  public class Global : System.Web.HttpApplication, IPamMetier

De hecho, los métodos GetAllIdentitesEmployes (línea 24) y GetSalaire (línea 30) son estáticos, mientras que los métodos de la interfaz IPamMetier no lo son. Por lo tanto, la clase Global no puede implementar la interfaz IPamMetier. Por otra parte, no es posible declarar los métodos GetAllIdentitesEmployes y GetSalaire como no estáticos. De hecho, se accede a ellos a través del nombre de la clase y no a través de una instancia de la misma.

  • Línea 15: el método Application_Start es análogo al de las clases [Global] estudiadas en las versiones anteriores. Instancia la capa [metier] (línea 18) y, a continuación, inicializa (línea 20) la matriz de empleados de la línea 12.
  • línea 24: el método GetAllIdentitesEmployes se limita a devolver la matriz de empleados de la línea 12. De ahí el interés de haberla memorizado desde el inicio de la aplicación.
  • línea 30: el método GetSalaire invoca el método del mismo nombre de la capa [metier].

Para instanciar la capa [metier] (línea 18), la clase [Global] utiliza el framework Spring. Este se configura mediante el archivo [Web.config], que es idéntico al del proyecto anterior: configura Spring y NHibernate para instanciar las capas [metier] y [dao] del servicio web.

Volvamos a la arquitectura de nuestra aplicación cliente/servidor:

En el lado del servidor, solo queda escribir el propio servicio web [S]. Si volvemos a la arquitectura de la aplicación:

vemos que, en el lado del servidor, todas las capas que preceden a la capa [metier] implementan su interfaz IPamMetier. No es obligatorio, pero es un enfoque que parece lógico. Este razonamiento se puede aplicar en el lado del cliente, al cliente [C] del servicio web [S]. De este modo, todas las capas que separan la capa [web] de la capa [metier] implementan entonces la interfaz IPamMetier. Se puede decir así que hemos vuelto a una aplicación de tres capas:

  • la capa de presentación [web] [1]
  • la capa [metier] [2]
  • la capa de acceso a datos [3]

La implementación del servicio web [Service1.asmx.cs] podría ser la siguiente:


using System.Web.Services;
using Pam.Dao.Entites;
using Pam.Metier.Entites;
using Pam.Metier.Service;

namespace pam_v5
{
  [WebService(Namespace = "http://st.istia.univ-angers.fr/")]
  [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
  [System.ComponentModel.ToolboxItem(false)]
  public class Service1 : System.Web.Services.WebService, IPamMetier
  {

    // lista de todas las identidades de los empleados 
    [WebMethod]
    public Employe[] GetAllIdentitesEmployes()
    {
      return Global.GetAllIdentitesEmployes();
    }

    // ------- el cálculo del salario 
    [WebMethod]
    public FeuilleSalaire GetSalaire(string ss, double heuresTravaillees, int joursTravailles)
    {
      return Global.GetSalaire(ss, heuresTravaillees, joursTravailles);
    }
  }
}
  • línea 8: la clase se anota con el atributo [WebService] y le damos un nombre al espacio de nombres del servicio web
  • línea 11: la clase [Service1] hereda de la clase [WebService] e implementa la interfaz [IPamMetier]
  • líneas 15 y 22: cada método de la clase se anota con el atributo [WebMethod] para que quede expuesto a los clients remotos. Por defecto, todos los métodos públicos de un servicio web quedan expuestos. Por lo tanto, los atributos de las líneas 15 y 22 son opcionales en este caso. Para implementar la interfaz [IPamMetier], cada método se limita a llamar al método del mismo nombre de la clase [Global].

Ya estamos listos para ejecutar el servicio web:

  • en [1], el proyecto se regenera
  • en [2], se selecciona el servicio web [Service1.asmx] y se muestra en el navegador [3]
  • en [4], la página web mostrada. Presenta los métodos del servicio web.
  • en [4], seguimos el enlace [GetAllIdentitesEmployes] y obtenemos en [5] la página de prueba de este método.
  • en [6], el URL del método
  • en [7], el botón [Appeler] que permite probar el método. Este no requiere ningún parámetro.
  • en [8], el resultado Xml devuelto por el servicio web. En este, solo las propiedades SS, Apellido y Nombre de los objetos Empleado son significativas, ya que el método [GetAllIdentitesEmployes] solo solicita estas propiedades. Sin embargo, este método devuelve una matriz de objetos Empleado. En [8] se observa que las propiedades numéricas Id y Version se incluyen en el flujo Xml devuelto, pero no las propiedades con valor nulo: Dirección, Ciudad, CodePostal y Indemnites.

Tenemos un servicio web activo. Ahora vamos a escribir un cliente C# para él. Para ello, necesitaremos el URI del archivo WSDL del servicio web. Lo obtenemos en la página que se muestra inicialmente al ejecutar [Service.asmx]:

  • en [1], el URI del servicio web
  • en [2], el enlace que lleva a su archivo WSDL
  • en [3], el valor de este enlace

9.3. El proyecto C# de un cliente NUnit del servicio web

Creamos un proyecto C# (con Visual C# y no con Visual Web Developer) para el cliente del servicio web. Será un cliente de prueba NUnit. Por lo tanto, el proyecto será de tipo «Biblioteca de clases».

  • En [1], creamos un proyecto C# de tipo «Biblioteca de clases»
  • en [2], le damos un nombre al proyecto
  • en [3], el proyecto. Eliminamos [Class1.cs].
  • en [4], el nuevo proyecto.
  • en las propiedades del proyecto, en la pestaña [Application] [5], establecemos el espacio de nombres del proyecto. Cada clase generada por el IDE se creará en este espacio.

Guardamos nuestro nuevo proyecto en la ubicación que nos convenga:

 

Una vez hecho esto, generamos el cliente del servicio web remoto. Para entender lo que vamos a hacer, hay que volver a la arquitectura cliente/servidor que estamos construyendo:

El IDE generará la capa de cliente [C] a partir del URI del archivo WSDL del servicio web [S]. Recordemos que el URI de este archivo se ha anotado anteriormente. Procedemos de la de la siguiente manera:

  • en [1], hacer clic con el botón derecho en la rama Referencias y añadir una referencia de servicio
  • en [2], indicar el URL del archivo WSDL del servicio web anotado anteriormente. Este debe iniciarse previamente si aún no lo está.
  • en [3], solicite el descubrimiento del servicio web a través de su archivo WSDL
  • en [4], el servicio web detectado
  • en [5], los métodos expuestos por el servicio web.
  • en [6], el espacio de nombres en el que se quieren colocar las clases e interfaces del cliente que se va a generar.
  • Se valida el asistente

  • en [1], el e e cliente generado. Hacemos doble clic sobre él para acceder a su contenido.
  • En [2], en el explorador de objetos, se muestran las clases e interfaces del espacio de nombres Client.WsPam. Este es el espacio de nombres del cliente generado.
  • En [3], la clase que implementa el cliente del servicio web.
  • En [4], los métodos implementados por el cliente [Service1SoapClient]. Aquí se encuentran los dos métodos del servicio web remoto [5] y [6].
  • En [2], se encuentran las imágenes de las entidades de las capas:
    • [metier]: FeuilleSalaire, ElementsSalaire
    • [dao]: Empleado, Cotisations, Indemnites

A continuación, hay que recordar que estas imágenes de las entidades remotas se encuentran en el lado del cliente y en el espacio de nombres PamV5Client.WsPam.

Examinemos los métodos y propiedades que expone una de ellas:

  • en [1], se selecciona la clase local [Employe]
  • en [2], encontramos las propiedades de la entidad remota [Employe], así como los campos privados utilizados para las necesidades propias de la entidad local.

Volvamos a nuestra aplicación C#. Le añadimos una clase de prueba NUnit:

  • en [1], se ha añadido la clase [NUnit]. La clase [NUnit] necesitará el marco NUnit y, por lo tanto, una referencia a DLL de este. Suponemos aquí que el framework NUnit se ha instalado en el equipo (http://nunit.org/).
  • En [2], se añade una referencia al proyecto
  • en la pestaña [3] .NET, que agrupa los DLL registrados en el equipo, seleccionamos [4], la DLL [nunit.framework] version 2.4.6 mínima.

Por otra parte, vamos a utilizar Spring para instanciar el cliente local [C] del servicio web [S]:

La referencia a DLL de Spring se puede añadir tal y como se hizo con el marco NUnit si los DLL se han registrado previamente en el equipo (http://www.springframework.net/download.html).

Procedemos de otra manera. Utilizamos la carpeta [lib] de los proyectos anteriores, que contenía los DLL necesarios para Spring, y añadimos la referencia de Spring al proyecto:

Volvamos a la arquitectura del cliente que estamos construyendo:

Arriba vemos que el cliente de prueba [1] interactúa con una capa [metier] [2] ampliada. Esta presenta los mismos métodos que la capa [metier] remota. Por lo tanto, podemos utilizar la clase de prueba que ya vimos al probar la capa [metier] en el proyecto C# [pam-metier-dao-nhibernate]:


using NUnit.Framework;
using Pam.Dao.Entites;
using Pam.Metier.Entites;
using Pam.Metier.Service;
using Spring.Context.Support;

namespace Pam.Metier.Tests {

    [TestFixture()]
    public class NunitTestPamMetier : AssertionHelper {

        // la capa [metier] que se va a probar 
        private IPamMetier pamMetier;

        // constructor
        public NunitTestPamMetier() {
            // instanciación de la capa [dao]
            pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
        }


        [Test]
        public void GetAllIdentitesEmployes() {
            // verificación del número de empleados 
            Expect(2, EqualTo(pamMetier.GetAllIdentitesEmployes().Length));
        }

        [Test]
        public void GetSalaire1() {
            // cálculo de una nómina 
            FeuilleSalaire feuilleSalaire = pamMetier.GetSalaire("254104940426058", 150, 20);
            // verificaciones 
            Expect(368.77, EqualTo(feuilleSalaire.ElementsSalaire.SalaireNet).Within(1E-06));
            // nómina de un empleado inexistente 
            bool erreur = false;
            try {
                feuilleSalaire = pamMetier.GetSalaire("xx", 150, 20);
            } catch (PamException) {
                erreur = true;
            }
            Expect(erreur, True);
        }

    }
}

Hay que realizar algunas modificaciones:

  • en la línea 18, se instancia la capa [metier] con el framework Spring. La clase no es la misma en ambos casos. Aquí, la capa local [metier] es una instancia de la clase [PamV5Client.WsPam.Service1SoapClient], la clase generada por IDE. Por lo tanto, Spring se configura de la siguiente manera en el archivo [app.config] del proyecto C#:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

    <configSections>
        <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="PamV5Client.WsPam.Service1SoapClient, pam-v5-client-csharp-webservice"/>
        </objects>
    </spring>


    <system.serviceModel>
...
  • línea 16 anterior, el objeto [pammetier] es una instancia de la clase [PamV5Client.WsPam.Service1SoapClient] que se encuentra en el ensamblado (assembly) [pam-v5-client-csharp-webservice]. Para obtener la primera información, basta con volver a la definición de la clase [Service1SoapClient] en el explorador de objetos (apartado 9.3):
  • en [2], la clase de implementación de la capa local [metier] y en [1] su espacio de nombres
  • en [3], en las propiedades del proyecto, el nombre del ensamblado, la segunda información necesaria para la configuración del objeto Spring [pammetier].

Volvamos al código de instanciación de la capa local [metier] en [NUnit.cs]:


        // la capa [metier] a probar 
        private IPamMetier pamMetier;

        // constructor
        public NunitTestPamMetier() {
            // instanciación de la capa [dao]
            pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
        }

Línea 7: la capa remota [metier] era de tipo IPamMetier. Aquí, la capa [metier] es de tipo [Service1SoapClient]:


public class Service1SoapClient : System.ServiceModel.ClientBase<Service1Soap>

Vemos que la clase Service1SoapClient no implementa la interfaz IPamMetier, aunque exponga métodos con el mismo nombre. Por lo tanto, debemos escribir la instanciación de la capa local [metier] de la siguiente manera:


        // la capa [metier] a probar 
        private Service1SoapClient pamMetier;

        // constructor
        public NunitTestPamMetier() {
            // instanciación de la capa [metier]
            pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as Service1SoapClient;
}

Otra modificación que hay que realizar:


        [Test]
        public void GetSalaire1() {
...
            try {
                feuilleSalaire = pamMetier.GetSalaire("xx", 150, 20);
            } catch (PamException) {
                erreur = true;
            }
            Expect(erreur, True);
        }

El código anterior utiliza, en la línea 6, el tipo PamException, que no existe en el lado del cliente. Lo sustituiremos por su clase padre, el tipo Exception.


        [Test]
        public void GetSalaire1() {
...
            try {
                feuilleSalaire = pamMetier.GetSalaire("xx", 150, 20);
            } catch (Exception) {
                erreur = true;
            }
            Expect(erreur, True);
        }

Por último, los espacios de nombres importados ya no son los mismos:


using System;
using PamV5Client.WsPam;
using NUnit.Framework;
using Spring.Context.Support;

Una vez hecho esto, se puede generar el proyecto de tipo «Biblioteca de clases». Se crea el siguiente DLL:

  • en [1], la carpeta [bin/Release] del proyecto C#
  • en [2], el DLL del proyecto.

A continuación, la prueba NUnit se ejecuta mediante el marco NUnit (la base MySQL dbpam_nhibernate debe estar activa para la prueba):

  • en [3] y [4], se carga DLL [2] en la aplicación de prueba NUnit
  • en [5], se selecciona y se ejecuta la clase de prueba [6]
  • en [7], los resultados de una prueba superada

Ahora tenemos un servicio web operativo.