Skip to content

10. A aplicação [SimuPaie] – versão 6 – cliente ASP.NET de um serviço web

10.1. A arquitetura da aplicação

Estamos a desenvolver a arquitetura cliente/servidor do teste NUnit da seguinte forma:

No [1], o teste NUnit é substituído pela aplicação web da versão 8. É importante lembrar que esta arquitetura inclui dois servidores web não representados:

  • um servidor web que executa o serviço web [S]. Este será executado numa primeira instância do Visual Web Developer.
  • um servidor web que executa o cliente web [1]. Este será executado numa segunda instância do Visual Web Developer.

10.2. O projeto do Visual Web Developer do cliente [web]

Criamos um novo projeto Web ASP.NET:

  • no [1], selecionamos um projeto Web em C#
  • em [2], selecionamos «Aplicação Web ASP.NET»
  • em [3], atribuímos um nome ao projeto web
  • No [4], indicamos uma localização para este projeto
  • em [5], o projeto criado

Alteramos algumas propriedades do projeto:

  • em [1], o nome do conjunto que será gerado
  • em [2], o espaço de nomes predefinido das classes e interfaces que serão criadas

Para compilar o projeto [pam-v6-client-webservice], podemos recuperar os ficheiros [Global.asax] e [Default.aspx] do projeto web [pam-v4-3tier-nhibernate-multivues-monopage] e copiá-los para o projeto [pam-v6-client-webservice]. Para tal, começa-se por utilizar o Explorador do Windows:

  • para [1], a pasta do projeto [pam-v4-3tier-nhibernate-multivues-monopage]
  • para [2], a pasta do projeto [pam-v6-client-webservice] após a cópia
    • das pastas [images, pam, ressources]
    • dos ficheiros [Global.asax, Global.asax.cs]
    • dos ficheiros [Default.aspx, Default.aspx.cs, Default.aspx.designer.cs]
  • para [3], no Visual Studio Express, exibe-se todos os ficheiros do projeto, para que apareçam os ficheiros recém-adicionados
  • no [4], incluímos no projeto as pastas e os ficheiros adicionados
  • em [5], o novo projeto [pam-v6-client-webservice]

Nesta fase, é possível gerar o projeto pela primeira vez ([6]). Obtêm-se os seguintes erros:

1
2
3
4
5
6
7
Erreur    1    Le type ou le nom d'espace de noms 'Dao' n'existe pas dans l'espace de noms 'Pam' (une référence d'assembly est-elle manquante ?)    C:\temp\pam-aspnet\pam-v6-client-webservice\Global.asax.cs    3    11    pam-v6-client-webservice
Erreur    2    Le type ou le nom d'espace de noms 'Metier' n'existe pas dans l'espace de noms 'Pam' (une référence d'assembly est-elle manquante ?)    C:\temp\pam-aspnet\pam-v6-client-webservice\Global.asax.cs    4    11    pam-v6-client-webservice
Erreur    3    Le type ou le nom d'espace de noms 'Dao' n'existe pas dans l'espace de noms 'Pam' (une référence d'assembly est-elle manquante ?)    c:\temp\pam-aspnet\pam-v6-client-webservice\default.aspx.cs    5    11    pam-v6-client-webservice
Erreur    4    Le type ou le nom d'espace de noms 'Spring' est introuvable (une directive using ou une référence d'assembly est-elle manquante ?)    C:\temp\pam-aspnet\pam-v6-client-webservice\Global.asax.cs    6    7    pam-v6-client-webservice
Erreur    5    Le type ou le nom d'espace de noms 'Metier' n'existe pas dans l'espace de noms 'Pam' (une référence d'assembly est-elle manquante ?)    c:\temp\pam-aspnet\pam-v6-client-webservice\default.aspx.cs    6    11    pam-v6-client-webservice
Erreur    6    Le type ou le nom d'espace de noms 'Employe' est introuvable (une directive using ou une référence d'assembly est-elle manquante ?)    C:\temp\pam-aspnet\pam-v6-client-webservice\Global.asax.cs    13    19    pam-v6-client-webservice
Erreur    7    Le type ou le nom d'espace de noms 'IPamMetier' est introuvable (une directive using ou une référence d'assembly est-elle manquante ?)    C:\temp\pam-aspnet\pam-v6-client-webservice\Global.asax.cs    14    19    pam-v6-client-webservice

Para compreender estes erros e corrigi-los, é necessário voltar à arquitetura do projeto web em desenvolvimento:

O projeto [pam-v6-client-webservice] corresponde à camada [web] [1] no esquema acima. Vemos que esta camada comunica com o cliente [C] do serviço web, um cliente que iremos gerar em breve.

  • Os erros 2, 5 e 7 devem-se ao facto de o código de [pam-v4] fazer referência a DLL da camada [metier], DLL, que agora se encontra no lado do servidor e não no lado do cliente.
  • Os erros 1 e 3 têm a mesma causa, mas, desta vez, dizem respeito à DLL da camada [dao]
  • o erro 4 deve-se ao facto de o código de [Global.asax] utilizar o Spring e de o nosso projeto não referenciar a DLL do Spring. Vamos adicionar essa referência.
  • O erro 6 deve-se ao facto de a classe [Employe] estar definida na camada [dao], que não existe no lado do cliente.

Os erros de espaço de nomes 1, 2, 4, 5, 6 e 7 serão resolvidos através da geração do cliente C do serviço web. O erro 3 é resolvido adicionando ao projeto uma referência à classe DLL do Spring. Podemos proceder da seguinte forma:

  • no [1], adiciona-se uma referência ao projeto [pam-v6-client-webservice]
  • no [4], adicionou-se uma referência ao DLL e ao [Spring.Core] da pasta [lib].

Após a adição desta referência no Spring, a geração do projeto apresenta menos um erro. Todos os outros erros devem-se a linhas de código que fazem referência a objetos das camadas [metier] e [dao], que agora se encontram no servidor. Veremos que estes objetos em falta serão gerados no cliente [C] do serviço web.

Antes de gerar o cliente [C] do serviço web, vamos alterar o espaço de nomes das diferentes classes presentes. Atualmente, estas encontram-se no espaço de nomes [pam-v4]. Alteramos este espaço de nomes para [pam-v6]. Os ficheiros em questão são os seguintes: Default.aspx.cs, Default.aspx.designer.cs, Global.asax.cs. Por exemplo:


....
namespace pam_v6
{
  public class Global : System.Web.HttpApplication
  {
    // --- dados estáticos da aplicação ---
    public static Employe[] Employes;
    public static IPamMetier PamMetier = null;
....

Na linha 2, o espaço de nomes pam_v4 foi substituído pelo espaço de nomes pam_v6.

Além disso, é necessário alterar a marcação dos ficheiros: Default.aspx e Global.asax:

  

A marcação de [Default.aspx] passa a ser:


<%@ 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">
.....

Na linha 1, o atributo Inherits indica o nome da classe do ficheiro [Default.aspx.cs]. Altera-se o espaço de nomes.

A marcação de [Global.asax] passa a ser:


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

No exemplo acima, altera-se o espaço de nomes do atributo Inherits.

Geramos agora o cliente [C] do serviço web.

Para realizar as operações seguintes, é necessário que o serviço web [S] [pam-v5-webservice] seja iniciado noutra instância do Visual Web Developer.

  • No [1], adiciona-se ao projeto [pam-v6-client-webservice] uma referência ao serviço web [pam-v5-webservice]
  • em [2], o URI do serviço web [pam-v5-webservice]. Trata-se do URL do ficheiro WSDL deste serviço web. Na criação do serviço web [pam-v5-webservice], foi explicado como obter esta URL.
  • No [3], solicita-se ao assistente que processe o ficheiro WSDL indicado no [2]
  • em [4], o serviço web [Service1Soap] descoberto e, em [5], os métodos remotos que este expõe.
  • em [6], define-se o espaço de nomes no qual as classes e interfaces do cliente [C] serão geradas
  • inicia-se a geração do cliente [C]
  • no [1], o cliente gerado. Clica-se duas vezes nele para aceder ao seu conteúdo.
  • No [2], no explorador de objetos, são apresentadas as classes e interfaces do espaço de nomes pam_v6.WsPam. Este é o espaço de nomes do cliente gerado.
  • em [3], a classe que implementa o cliente do serviço web.
  • Em [4], os métodos implementados pelo cliente [Service1SoapClient]. Aqui encontram-se os dois métodos do serviço web remoto [5] e [6].
  • No ficheiro [2], encontram-se as imagens das entidades das camadas:
    • [metier]: FeuilleSalaire, ElementsSalaire
    • [dao]: Employe, Cotisations, Indemnites

A seguir, é importante ter em conta que estas imagens das entidades remotas encontram-se no lado do cliente e no espaço de nomes pam_v6.WsPam.

Analisemos os métodos e propriedades expostos por uma delas:

  • em [1], seleciona-se a classe local [Employe]
  • em [2], encontram-se as propriedades da entidade remota [Employe], bem como campos privados utilizados para as necessidades específicas da entidade local.

Analisemos o código de [Global.asax.cs], que apresenta erros:

 
  • as linhas 3 e 4 utilizam espaços de nomes que existem no servidor, mas não no cliente.
  • a linha 13 utiliza uma classe [Employe] cujo espaço de nomes correto não foi declarado
  • a linha 14 utiliza uma interface IPamMetier desconhecida no cliente.

Já nos deparámos com problemas semelhantes no cliente C# analisado anteriormente.

Na arquitetura:

  • a camada local [metier] é implementada pelo cliente [C] do tipo pam_v6.WsPam.Service1SoapClient, que não implementa a interface IPamMetier, embora, por outro lado, apresente métodos com os mesmos nomes.
  • As entidades manipuladas (Employe, Indemnites, Cotisations) pelo cliente [C] gerado encontram-se no espaço de nomes pam_v6.WsPam

O código de [Global.asax.cs] evolui da seguinte forma:


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
  {
    // --- dados estáticos da aplicação ---
    public static Employe[] Employes;
    public static Service1SoapClient PamMetier = null;
    public static string Msg;
    public static bool Erreur = false;

    // inicialização da aplicação
    public void Application_Start(object sender, EventArgs e)
    {
      // processamento do ficheiro de configuração
      try
      {
        // instanciação da camada [metier]
        PamMetier = ContextRegistry.GetContext().GetObject("pammetier") as Service1SoapClient;
        // lista simplificada de funcionários
        Employes = PamMetier.GetAllIdentitesEmployes();
        // Operação bem-sucedida
        Msg = "Base chargée...";
      }
      catch (Exception ex)
      {
        // regista-se o erro
        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)
    {
      // insere-se uma lista de simulações vazia na sessão 
      List<Simulation> simulations = new List<Simulation>();
      Session["simulations"] = simulations;
    }
  }
}

A linha 24 instancia a camada [C] através do Spring. A configuração necessária para esta instanciação é efetuada em [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>

Na linha 16, a camada [metier] é uma instância da classe [pam_v6.WsPam.Service1SoapClient], que se encontra em DLL [pam-v6-client-webservice]. Recorde-se que configurámos o projeto [pam-v6-client-webservice] para que gerasse esta DLL.

Ainda existem alguns erros no [Default.aspx.cs]:

  • linhas 5 e 6: estes espaços de nomes já não existem. As entidades encontram-se agora no espaço de nomes pam_v6 ou pam_v6.WsPam.
  • linha 98: o cliente gerado [C] não herdou a classe [PamException] da camada [dao]. Não o podia fazer, uma vez que o serviço web não expõe esta exceção. Opta-se por substituir PamException pela sua classe pai Exception.

O código passa a ser:


...
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)
      {
...

Depois de corrigidos estes erros, podemos executar a aplicação web:

  • em [1], o URL do cliente web do serviço web remoto
  • em [2], o menu suspenso dos funcionários foi preenchido. Os dados que contém provêm do serviço web.

Convidamos o leitor a testar esta versão 6, cópia da versão 4.