9. A aplicação [SimuPaie] – versão 5 – ASP.NET / serviço web
Leituras recomendadas: referência [2], Introdução ao C# 2008, capítulo 10 “Serviços da Web”
9.1. A nova arquitetura da aplicação
A arquitetura em camadas da aplicação Pam é atualmente a seguinte:
![]() |
Vamos evoluí-la da seguinte forma:
![]() |
Enquanto na arquitetura anterior as camadas [web], [metier] e [dao] eram executadas na mesma máquina virtual,NET, na nova arquitetura, a camada [web] será executada em uma máquina virtual diferente daquelas em que as camadas [metier] e [dao] são executadas. Esse será o caso, especialmente, se a camada [web] estiver em uma máquina M1 e as camadas [metier] e [dao] estiverem em uma máquina M2. Temos aqui uma arquitetura cliente/servidor:
- o servidor é composto pelas camadas [metier] e [dao]. Por se tratar de um serviço web, ele precisa do servidor web nº 2 para ser executado.
- O cliente é composto pela camada [web]. Para ser executado, ele precisa do servidor web nº 1.
- O cliente e o servidor se comunicam pela rede TCP/IP com o protocolo HTTP / SOAP. Para isso, duas novas camadas devem ser adicionadas à arquitetura:
- a camada [S], que será um serviço web. O serviço web recebe as solicitações dos clientes remotos e utiliza as camadas [metier] e [dao] para atendê-las. Existem diversas maneiras de construir um serviço TCP/IP. A vantagem do serviço web é dupla:
- ele utiliza o protocolo HTTP, que é permitido pelos firewalls de empresas e órgãos públicos
- ele utiliza um subprotocolo HTTP / SOAP padrão, implementado por diversas plataformas de desenvolvimento: .Net, Java, PHP, Flex, ... Assim, um serviço web pode ser “consumido” (esse é o termo comum) por clientes .Net, Java, PHP, Flex, etc.
- a camada [C], que será o cliente do serviço web remoto. Sua função será se comunicar com o serviço web [S].
- a camada [S], que será um serviço web. O serviço web recebe as solicitações dos clientes remotos e utiliza as camadas [metier] e [dao] para atendê-las. Existem diversas maneiras de construir um serviço TCP/IP. A vantagem do serviço web é dupla:
Essa nova arquitetura pode ser derivada das anteriores sem muito esforço:
- as camadas [metier] e [dao] permanecem inalteradas
- a camada [web] sofre uma ligeira evolução, essencialmente para referenciar entidades como Employe e FeuilleSalaire, que passaram a ser entidades da camada cliente [C]. Essas entidades são análogas às das camadas [metier] ou [dao], mas pertencem a espaços de nomes diferentes.
- A camada de servidor [S] é uma classe que implementa a interface IPamMetier da camada [metier]. Essa implementação limita-se a chamar os métodos correspondentes da camada [metier]. Os métodos implementados pela camada de servidor [S] serão “expostos” aos clientes remotos, que poderão chamá-los.
- A camada cliente [C] será gerada pelo Visual Studio.
Os princípios da nova arquitetura são os seguintes:
- a camada [web] continua a se comunicar com a camada [metier] como se esta fosse local. Para isso, a camada cliente [C] implementa ainterface IPamMetier da camada [metier] real e se apresenta à camada [web] como uma camada [metier] local. Além do problema dos espaços de nomes mencionado anteriormente, a camada [web] permanece inalterada. Essa é a vantagem de se trabalhar em camadas. Se tivéssemos construído um aplicativo de camada única, seria necessário reformulá-lo profundamente.
- A camada cliente [C] transmite, de forma transparente para a camada [web], as solicitações desta última ao serviço web remoto [S]. Ela é responsável por todo o aspecto de “comunicação de rede”. Recebe uma resposta do serviço web remoto, que ela formata para entregá-la à camada [web] no formato esperado por esta.
- No lado do servidor, o serviço web [S] recebe comandos de seus clientes remotos. Ele os formata para chamar os métodos da interface IPamMetier da camada [metier]. Ao receber a resposta da camada [metier], ele a formata para transmiti-la pela rede ao cliente [C]. As camadas [metier] e [dao] não precisam ser modificadas.
9.2. O projeto do Visual Web Developer para o serviço web
Estamos criando um novo projeto com o Visual Web Developer:
![]() |
- em [1], selecionamos um projeto web em C#
- em [2], selecionamos “Aplicativo de serviço web ASP.NET”
- em [3], nomeamos o projeto web
- em [4], indicamos um local para esse projeto
![]() |
- em [1], o projeto gerado. Trata-se de um projeto web clássico, com as seguintes particularidades:
- foi especificado que o projeto é do tipo “serviço web”. Um serviço web não envia páginas da web HTML aos seus clientes, mas sim dados no formato XML. Portanto, a página [Default.aspx], que normalmente seria gerada, não foi criada.
- Em [2], foi gerado um arquivo [Service1.asmx] com o seguinte conteúdo:
<%@ WebService Language="C#" CodeBehind="Service1.asmx.cs" Class="pam_v5_webservice.Service1" %>
- (continuação)
- - a tag WebService indica que [Service.asmx] é um serviço web
- - o atributo CodeBehind indica a localização do código-fonte desse serviço web
- - o atributo Class indica o nome da classe que implementa o serviço web no código-fonte
O código-fonte [Service.asmx.cs] do serviço web gerado por padrão é o seguinte:
using System.Web.Services;
namespace pam_v5_webservice
{
/// <summary>
/// Descrição resumida do Serviço1
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// Para permitir a chamada deste serviço web a partir de um script usando ASP.NET AJAX, remova os símbolos de comentário da linha a seguir.
// [System.Web.Script.Services.ScriptService]
public class Service1 : System.Web.Services.WebService
{
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
}
}
- linha 8: a anotação WebService, que faz com que a classe Service1 da linha 13 seja exposta como um serviço web. Um serviço web pertence a um espaço de nomes para evitar que dois serviços web em todo o mundo tenham o mesmo nome. Teremos que alterar esse espaço de nomes posteriormente.
- linha 13: a classe Service1 deriva da classe WebService do framework .NET.
- linha 16: a anotação WebMethod faz com que o método assim anotado seja exposto aos clientes remotos, que poderão, assim, chamá-lo.
- linhas 17-20: o método HelloWorld é um método de demonstração. Nós o excluiremos mais tarde. Ele nos permite realizar os primeiros testes e conhecer as ferramentas do Visual Studio, bem como alguns aspectos importantes sobre serviços web.
![]() |
- no [1], executamos o serviço web [Service.asmx]
![]() |
- VS O Web Developer iniciou seu servidor web integrado e o configurou para escutar em uma porta aleatória, neste caso, a 1599. A URL [2] foi então solicitada ao servidor web. Trata-se da URL de uma página de teste do serviço web.
- em [3], um link que permite visualizar o arquivo de descrição do serviço web. Esse arquivo, chamado WSDL (WebService Description Language) devido à sua extensão (.wsdl), é um arquivo XML que descreve os métodos expostos pelo serviço web. É a partir desse arquivo WSDL que os clientes podem conhecer:
- o namespace do serviço web
- a lista de métodos expostos pelo serviço web
- os parâmetros esperados por cada um deles
- a resposta retornada por cada um deles
- no [4], o único método exposto pelo serviço web .
![]() |
- em [5], o conteúdo do arquivo WSDL obtido por meio do link [3]. Destaque para o URL e o [6]. É necessário que os usuários do serviço web tenham conhecimento desses arquivos.
![]() |
- em [7], a página obtida ao acessar o link [4] permite chamar o método [HelloWorld] do serviço web
- em [8], o resultado obtido: uma resposta XML. Observe-se o URL [9] do método.
A análise das páginas anteriores permite compreender como um método de um serviço web é chamado e que tipo de resposta ele retorna. Isso permite escrever clientes HTTP capazes de interagir com o serviço web. A maioria dos IDE atuais permite a geração automática desse cliente HTTP, poupando assim o desenvolvedor de ter que escrevê-lo. Esse é o caso, notadamente, do Visual Studio Express.
Antes de prosseguir com este projeto, vamos alterar o namespace utilizado por padrão durante a geração das classes:
![]() |
Ao selecionarmos as propriedades do projeto (clique com o botão direito do mouse no projeto / Propriedades), vemos em [1] o nome do projeto e em [2] seu namespace padrão.
Feito isso,
- em [Service1.asmx.cs], alteramos o namespace da classe:
using System.Web.Services;
namespace pam_v5
{
...
public class Service1 : System.Web.Services.WebService
{
...
}
}
- em [Service.asmx], alteramos também o namespace utilizado para a classe [Service1] (clique com o botão direito do mouse / exibir marcação):
<%@ WebService Language="C#" CodeBehind="Service1.asmx.cs" Class="pam_v5.Service1" %>
Voltemos à arquitetura do nosso aplicativo:
![]() |
- a camada [S] é o serviço web. Ela se limita a expor os métodos da camada [metier] a clientes remotos. É essa camada que estamos construindo.
- A camada [C] é o cliente HTTP do serviço web. É essa camada que os IDE sabem gerar automaticamente.
- A camada [web] vê a camada [C] como uma camada [metier] local, desde que a camada [C] implemente ainterface da camada remota [metier].
Vemos a seguir que nosso serviço web irá:
- expor os métodos da camada [metier]
- interagir com esta última, que, por sua vez, irá interagir com a camada [dao].
O projeto deve, portanto, utilizar os DLL das camadas [metier] e [dao]. Ele evolui da seguinte forma:
![]() |
- para [1], adicionam-se referências ao projeto
- em [2], selecionam-se as DLL habituais da pasta [lib]. Deve-se certificar-se de que todas elas tenham a propriedade “Cópia local” definida como True. Os DLL selecionados são aqueles que implementam as camadas [metier] e [dao] com suporte NHibernate.
Um aplicativo web do tipo “serviço web ASP.NET” pode ter uma classe de aplicativo global “Global.asax”, assim como um aplicativo “site web ASP.NET” convencional. Vimos a vantagem dessa classe:
- ela é instanciada ao iniciar a aplicação e permanece na memória
- assim, pode armazenar dados compartilhados por todos os clientes e que são somente para leitura. Em nossa aplicação, ela armazenará, assim como nas anteriores, a lista simplificada de funcionários. Isso evitará a necessidade de buscar essa lista no banco de dados quando um cliente a solicitar.
![]() |
- em [1], clique com o botão direito do mouse no projeto
- em [2], selecione a opção [Ajouter un nouvel élément]
- em [3], selecione [Classe d'application globale]
- em [4], o arquivo [Global.asax] foi adicionado ao projeto
O conteúdo do arquivo [Global.asax] é o seguinte:
<%@ Application Codebehind="Global.asax.cs" Inherits="pam_v5.Global" Language="C#" %>
O conteúdo do arquivo [Global.asax.cs] é o seguinte:
using System;
namespace pam_v5
{
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
}
...
}
}
O que devemos fazer no método Application_Start? Exatamente a mesma coisa que nas aplicações web anteriores. Voltemos à arquitetura da aplicação e posicionemos nela a classe [Global]:
![]() |
No esquema acima,
- a classe [Global] é instanciada quando o serviço web é iniciado. Ela permanece na memória enquanto o serviço web estiver ativo.
- A classe [Global] instancia as camadas [metier] e [dao] em seu método [Application_Start]
- Para melhorar o desempenho, a classe [Global] armazena a lista simplificada de funcionários em um campo interno. Ela fornecerá a lista de funcionários a partir desse campo.
- O serviço web, por sua vez, é instanciado a cada solicitação de um cliente. Ele é encerrado após atender a solicitação. Ele não se comunicará diretamente com a camada [metier], mas sim com a classe [Global]. Esta implementará a interface da camada [metier].
A classe [Global] é análoga àquela já criada para as aplicações 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
{
// --- dados estáticos do aplicativo ---
public static Employe[] Employes;
public static IPamMetier PamMetier = null;
protected void Application_Start(object sender, EventArgs e)
{
// instanciação da camada [metier]
PamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
// recuperamos a tabela simplificada dos funcionários
Employes = PamMetier.GetAllIdentitesEmployes();
}
// lista simplificada de funcionários
static public Employe[] GetAllIdentitesEmployes()
{
return Employes;
}
// salário de um funcionário
static public FeuilleSalaire GetSalaire(string SS, double heuresTravaillées, int joursTravailles)
{
return PamMetier.GetSalaire(SS, heuresTravaillées, joursTravailles);
}
}
}
A classe [Global] implementa a interface [IPamMetier], mas isso não é explicitado na declaração:
public class Global : System.Web.HttpApplication, IPamMetier
De fato, os métodos GetAllIdentitesEmployes (linha 24) e GetSalaire (linha 30) são estáticos, enquanto os métodos da interface IPamMetier não o são. Portanto, a classe Global não pode implementar a interface IPamMetier. Além disso, não é possível declarar os métodos GetAllIdentitesEmployes e GetSalaire como não estáticos. Na verdade, eles são acessados por meio do nome da classe e não por meio de uma instância dela.
- linha 15: o método Application_Start é semelhante ao das classes [Global] analisadas nas versões anteriores. Ele instancia a camada [metier] (linha 18) e, em seguida, inicializa (linha 20) a matriz de funcionários da linha 12.
- linha 24: o método GetAllIdentitesEmployes limita-se a retornar a matriz de funcionários da linha 12. É por isso que vale a pena armazená-la na memória logo no início da aplicação.
- linha 30: o método GetSalaire chama o método de mesmo nome da camada [metier].
Para instanciar a camada [metier] (linha 18), a classe [Global] utiliza o framework Spring. Este framework é configurado pelo arquivo [Web.config], que é idêntico ao do projeto anterior: ele configura o Spring e o NHibernate para instanciar as camadas [metier] e [dao] do serviço web.
Voltemos à arquitetura de nossa aplicação cliente/servidor:
![]() |
No lado do servidor, resta apenas escrever o próprio serviço web [S]. Se voltarmos à arquitetura da aplicação:
![]() |
vemos que, no lado do servidor, todas as camadas que precedem a camada [metier] implementam a interface desta última, IPamMetier. Isso não é obrigatório, mas é uma abordagem que parece lógica. Esse raciocínio pode ser aplicado no lado do cliente, ao cliente [C] do serviço web [S]. Assim, todas as camadas que separam a camada [web] da camada [metier] implementam, então, a interface IPamMetier. Pode-se dizer, portanto, que voltamos a uma aplicação de três camadas:
- a camada de apresentação [web] [1]
- a camada [metier] [2]
- a camada de acesso aos dados [3]
A implementação do serviço web [Service1.asmx.cs] poderia ser a seguinte:
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 as identidades dos funcionários
[WebMethod]
public Employe[] GetAllIdentitesEmployes()
{
return Global.GetAllIdentitesEmployes();
}
// ------- o cálculo do salário
[WebMethod]
public FeuilleSalaire GetSalaire(string ss, double heuresTravaillees, int joursTravailles)
{
return Global.GetSalaire(ss, heuresTravaillees, joursTravailles);
}
}
}
- linha 8: a classe é anotada com o atributo [WebService] e atribuímos um nome ao espaço de nomes do serviço web
- linha 11: a classe [Service1] herda da classe [WebService] e implementa a interface [IPamMetier]
- linhas 15 e 22: cada método da classe é anotado com o atributo [WebMethod] para ser exposto aos clientes remotos. Por padrão, todos os métodos públicos de um serviço web são expostos. Os atributos das linhas 15 e 22 são, portanto, opcionais neste caso. Para implementar a interface [IPamMetier], cada método se limita a chamar o método de mesmo nome da classe [Global].
Estamos prontos para a execução do serviço web:
![]() |
- em [1], o projeto é regenerado
- em [2], seleciona-se o serviço web [Service1.asmx] e exibe-se no navegador [3]
- em [4], a página da web exibida. Ela apresenta os métodos do serviço web.
![]() |
- em [4], seguimos o link [GetAllIdentitesEmployes] e obtemos, em [5], a página de teste desse método.
- em [6], o URL do método
- em [7], o botão [Appeler] que permite testar o método. Este não requer nenhum parâmetro.
- em [8], o resultado XML retornado pelo serviço web. Nele, apenas as propriedades SS, Nom, Prenom dos objetos Employe são relevantes, pois o método [GetAllIdentitesEmployes] solicita apenas essas propriedades. No entanto, esse método retorna uma matriz de objetos Employe. Observa-se em [8] que as propriedades numéricas Id e Version estão no fluxo XML retornado, mas não as propriedades com valor null: Adresse, Ville, CodePostal, Indemnites.
Temos um serviço web ativo. Agora vamos criar um cliente em C# para ele. Para isso, precisaremos do URI do arquivo WSDL do serviço web. Nós o obtemos na página exibida inicialmente ao executar o [Service.asmx]:
![]() |
- no [1], o URI do serviço web
- em [2], o link que leva ao seu arquivo WSDL
- em [3], o valor desse link
9.3. O projeto em C# de um cliente NUnit do serviço web
Criamos um projeto em C# (com o Visual C# e não com o Visual Web Developer) para o cliente do serviço web. Será um cliente de teste NUnit. Portanto, o projeto será do tipo “Biblioteca de classes”.
![]() |
- no [1], criamos um projeto em C# do tipo “Biblioteca de classes”
- em [2], nomeamos o projeto
- em [3], o projeto. Excluímos [Class1.cs].
- em [4], o novo projeto.
![]() |
- nas propriedades do projeto, na aba [Application] [5], definimos o namespace do projeto. Cada classe gerada pelo IDE será criada nesse namespace.
Salvamos nosso novo projeto em um local de nossa preferência:
Feito isso, geramos o cliente do serviço web remoto. Para entender o que vamos fazer, é preciso voltar à arquitetura cliente/servidor que estamos construindo:
![]() |
O IDE irá gerar a camada cliente [C] a partir do URI do arquivo WSDL do serviço web [S]. Vale lembrar que o URI desse arquivo já havia sido registrado anteriormente. Procedemos a partir do da seguinte maneira:
![]() |
- para [1], clicar com o botão direito do mouse no ramo References e adicionar uma referência de serviço
- em [2], indique o URL do arquivo WSDL do serviço web anotado anteriormente. Este deve ser iniciado previamente, caso ainda não esteja em execução.
- em [3], solicitar a descoberta do serviço web por meio de seu arquivo WSDL
- no [4], o serviço web descoberto
- em [5], os métodos expostos pelo serviço web.
- em [6], o espaço de nomes no qual se deseja colocar as classes e interfaces do cliente que será gerado.
- confirma-se o assistente
![]() |
- em [1], o cliente gerado. Clique duas vezes nele para acessar seu conteúdo.
- Em [2], no explorador de objetos, são exibidas as classes e interfaces do espaço de nomes Client.WsPam. Esse é 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]. Nele, encontram-se os dois métodos do serviço web remoto [5] e [6].
- Em [2], encontram-se as imagens das entidades das camadas:
- [metier]: FeuilleSalaire, ElementsSalaire
- [dao]: Employe, Cotisations, Indemnites
A seguir, é importante lembrar que essas imagens das entidades remotas estão localizadas no lado do cliente e no espaço de nomes PamV5Client.WsPam.
Vamos examinar os métodos e propriedades expostos por uma delas:
![]() |
- em [1], seleciona-se a classe local [Employe]
- em [2], encontramos as propriedades da entidade remota [Employe], bem como campos privados utilizados para as necessidades específicas da entidade local.
Voltemos à nossa aplicação em C#. Adicionamos a ela uma classe de teste NUnit:
![]() |
- Na classe [1], foi adicionada a classe [NUnit]. A classe [NUnit] precisará do framework NUnit e, portanto, de uma referência à classe DLL deste framework. Supomos aqui que o framework NUnit tenha sido instalado no computador (http://nunit.org/).
- No [2], adiciona-se uma referência ao projeto
- na aba [3]. No NET, que reúne os DLL registrados no computador, selecionamos o [4], o DLL e o [nunit.framework], versão mínima 2.4.6.
Além disso, vamos usar o Spring para instanciar o cliente local [C] do serviço web [S]:
![]() |
A referência ao DLL do Spring pode ser adicionada da mesma forma que foi feito com o framework NUnit, caso os DLL tenham sido previamente registrados no computador (http://www.springframework.net/download.html).
Procedemos de maneira diferente. Utilizamos a pasta [lib] dos projetos anteriores, que continha os DLL necessários para o Spring, e adicionamos a referência do Spring ao projeto:
![]() |
Voltemos à arquitetura do cliente que está sendo desenvolvido:
![]() |
Acima, vemos que o cliente de teste [1] se conecta a uma camada [metier] [2] estendida. Essa camada apresenta os mesmos métodos que a camada remota [metier]. Portanto, podemos utilizar a classe de teste já apresentada durante o teste da camada [metier] no projeto 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 {
// a camada [metier] a ser testada
private IPamMetier pamMetier;
// construtor
public NunitTestPamMetier() {
// instanciação da camada [dao]
pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
}
[Test]
public void GetAllIdentitesEmployes() {
// verificação do número de funcionários
Expect(2, EqualTo(pamMetier.GetAllIdentitesEmployes().Length));
}
[Test]
public void GetSalaire1() {
// cálculo de uma folha de pagamento
FeuilleSalaire feuilleSalaire = pamMetier.GetSalaire("254104940426058", 150, 20);
// verificações
Expect(368.77, EqualTo(feuilleSalaire.ElementsSalaire.SalaireNet).Within(1E-06));
// folha de pagamento de um funcionário inexistente
bool erreur = false;
try {
feuilleSalaire = pamMetier.GetSalaire("xx", 150, 20);
} catch (PamException) {
erreur = true;
}
Expect(erreur, True);
}
}
}
Há algumas alterações a serem feitas:
- na linha 18, instanciamos a camada [metier] com o framework Spring. A classe não é a mesma nos dois casos. Aqui, a camada local [metier] é uma instância da classe [PamV5Client.WsPam.Service1SoapClient], a classe gerada pelo IDE. Portanto, o Spring está configurado da seguinte maneira no arquivo [app.config] do projeto 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>
...
- na linha 16 acima, o objeto [pammetier] é uma instância da classe [PamV5Client.WsPam.Service1SoapClient], que se encontra no assembly [pam-v5-client-csharp-webservice]. Para obter a primeira informação, basta voltar à definição da classe [Service1SoapClient] no explorador de objetos (parágrafo 9.3):
![]() |
- em [2], a classe de implementação da camada local [metier] e em [1] seu espaço de nomes
- em [3]; nas propriedades do projeto, o nome do assembly, a segunda informação necessária para a configuração do objeto Spring [pammetier].
Voltemos ao código de instanciação da camada local [metier] em [NUnit.cs]:
// a camada [metier] a ser testada
private IPamMetier pamMetier;
// construtor
public NunitTestPamMetier() {
// instanciação da camada [dao]
pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
}
Na linha 7, a camada remota [metier] era do tipo IPamMetier. Aqui, a camada [metier] é do tipo [Service1SoapClient]:
public class Service1SoapClient : System.ServiceModel.ClientBase<Service1Soap>
Vemos que a classe Service1SoapClient não implementa a interface IPamMetier, mesmo que exponha métodos com o mesmo nome. Portanto, precisamos escrever a instanciação da camada local [metier] da seguinte maneira:
// a camada [metier] a ser testada
private Service1SoapClient pamMetier;
// construtor
public NunitTestPamMetier() {
// instanciação da camada [metier]
pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as Service1SoapClient;
}
Outra modificação a ser feita:
[Test]
public void GetSalaire1() {
...
try {
feuilleSalaire = pamMetier.GetSalaire("xx", 150, 20);
} catch (PamException) {
erreur = true;
}
Expect(erreur, True);
}
O código acima utiliza, na linha 6, o tipo PamException, que não existe no lado do cliente. Vamos substituí-lo por sua classe pai, o tipo Exception.
[Test]
public void GetSalaire1() {
...
try {
feuilleSalaire = pamMetier.GetSalaire("xx", 150, 20);
} catch (Exception) {
erreur = true;
}
Expect(erreur, True);
}
Por fim, os namespaces importados não são mais os mesmos:
using System;
using PamV5Client.WsPam;
using NUnit.Framework;
using Spring.Context.Support;
Feito isso, o projeto do tipo “Biblioteca de classes” pode ser gerado. É criado o seguinte DLL:
![]() |
- em [1], a pasta [bin/Release] do projeto C#
- em [2], o DLL do projeto.
O teste NUnit é então executado pelo framework NUnit (a base MySQL dbpam_nhibernate deve estar ativa para o teste):
- em [3] e [4], a DLL [2] é carregada no aplicativo de teste NUnit
![]() |
- no [5], a classe de teste é selecionada e executada [6]
- em [7], os resultados de um teste bem-sucedido
Agora temos um serviço web operacional.






























