WCF - Implementando um serviço WCF em camadas no mundo real


 Neste artigo eu vou mostrar como podemos implementar um serviço WCF separando a camada de interface da camada da lógica de negócios e da camada de acesso a dados.

   

Neste artigo eu não vou me ater às definições e conceitos sobre a tecnologia WCF. Para conhecer os conceitos básicos de um serviço WCF leia os meus artigos publicados:

Este artigo será essencialmente prático mostrando como criar um serviço WCF em camadas em como consumir o serviço.

Observe que o serviço que iremos criar é somente uma versão simplificada de um serviço WCF usado em uma aplicação comercial em produção. No mundo real um serviço WCF com certeza envolverá mais cenários customizados e mais lógica de negócio.

Um aspecto importante do projeto SOA é que os limites do serviço devem ser explícitos, o que significa que ele esconde todos os detalhes da implementação por trás da fronteira de serviço. Isto inclui revelar qual tecnologia particular foi usada.

Além disso, dentro da implementação de um serviço, o código responsável pela manipulação de dados deve ser separado do código responsável pela lógica de negócios.

Por isso, no mundo real, é sempre uma boa prática implementar um WCF serviço em três ou mais camadas. As três camadas são a camada de interface de serviço, a camada de lógica de negócios, e a camada de acesso a dados.

Para simplificar o exemplo eu irei criar um serviço WCF em 3 camadas com alguma funcionalidade e com a mínima lógica de negócio possível. Essa abordagem, adotada para fins de aprendizagem, não impede que você adquira as habilidades básicas para poder customizar sua própria solução aplicando os conceitos às suas necessidades.

Dessa forma vamos definir as tarefas que iremos realizar neste artigo iniciando com a criação de um projeto de serviço WCF, usando um modelo de Biblioteca de Serviço WCF e também vamos:

• Criar os contratos de operação de serviços
• Criar os contratos de dados
• Adicionar um projeto BDO - Product Business Domain Object
• Adicionar um projeto camada de lógica de negócio
• Chamar a camada de lógica de negócios a partir da camada de interface de serviço
• Testar o serviço

Recursos usados:

Criando a solução e o projeto usando os templates WCF

Abra o VS 2013 Professional e clique em New Project;

Selecione a linguagem Visual C# e o template WCF;

A seguir selecione o template WCF Service Library e informe o nome NorthwindService e clique no botão OK;

Será criada uma solução contendo um projeto WCF conforme podemos ver na janela Solution Explorer:

O projeto já possui um arquivo IService1.cs para definir a interface do serviço e uma classe Service1.cs para implementar o serviço. Temos também um arquivo App.Config que iremos customizar mais adiante.

Este projeto é na verdade uma aplicação contendo um serviço WCF, uma aplicação hosting (WcfSvcHost) e um cliente WCF Test. Assim não precisamos escrever qualquer código para hospedar o projeto, e tão logo tenhamos implementado um serviço podemos usar o cliente WCF Test embutido para invocá-lo.

Criando a camada de interface de Serviço

Vamos agora criar os contratos da camada de interface de serviço. Para isso vamos utilizar os arquivos que já foram criados no projeto customizando-os para criar os contratos de serviço.

Vamos iniciar a customização alterando o nome da interface IService.cs para IProdutoService.cs.

A seguir vamos abrir o arquivo IProdutoService.cs e fazer o seguinte:

1- alterar a primeira operação do contrato de string GetData(int value)  para   Produto GetProduto(int id)

2- alterar a segunda operação do contrato de :  CompositeType GetDataUsingDataContract(CompositeType composite);   para bool AtualizaProduto(Produto produto, ref string mensagem);

Ao final o código da interface IProdutoService.cs deverá ficar da seguinte forma:

using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace NorthwindService
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
    [ServiceContract]
    public interface IProdutoService
    {
        [OperationContract]
        Produto GetProduto(int id);
        [OperationContract]
        bool AtualizaProduto(Produto produto, ref string mensagem); 
        // TODO: Add your service operations here
    }
.....
.....

Com esses alterações definimos dois contratos de serviços :

  1. O primeiro pode ser usado para obter detalhes de um produto para um produto com um ID específico;
  2. O segundo pode ser usado para atualizar um produto específico;

Observe que ainda não definimos o tipo Produto que usamos para definir os contratos de serviços. Vamos fazer isso mais adiante.

Criando os contratos de dados

Outro aspecto importante do projeto SOA é que você não deve assumir que a aplicação que vai consumir o serviço suporte um modelo de objeto complexo.

Uma parte da definição do serviço é a definição do contrato de dados para os tipos complexos que irão ser passados como parâmetros da operação ou dos valores de retorno.

Para obter o máximo de interoperabilidade e alinhamento com os princípios SOA, você não deve passar quaisquer tipos específicos definidos na plataforma .NET, como DataSet, DataTable ou Exceptions através da fronteira do serviço, pois o serviço poderia ser chamado por clientes em diferentes sistemas operacionais os quais não entenderiam os tipos específicos da plataforma .NET.

Assim, você deve definir objetos com uma estrutura de dados simples contendo somente propriedades primitivas. Você pode passar objetos aninhados com tipos complexos, tais como "Cliente com uma coleção de Pedidos".
No entanto, você não deve fazer qualquer suposição sobre se o consumidor esta apto a suportar construções orientadas a objeto, como herança, classes base, etc.

No nosso exemplo vamos criar um tipo de dados complexo para representar um objeto Produto. Dessa forma vamos definir um contrato de dados com 5 propriedades:

  1. ProdutoId
  2. ProdutoNome
  3. ProdutoQuantidade
  4. ProdutoPreco
  5. ProdutoAtivo

Essas propriedades serão usadas para com a aplicação cliente. Por exemplo, um fornecedor pode chamar um web service para atualizar o preço de um produto ou desativar um produto.

É preferível colocar os contratos de dados em arquivos separados com assembly separados, para para simplificar o exemplo, vamos por o DataContrat no mesmo arquivo que o contrato de serviço.

Vamos então alterar o arquivo iProdutoService, deletar o DataContract CompositeType existente e criar um DataContract Produto, conforme abaixo:

  [DataContract]
    public class Produto
    {
        [DataMember]
        public int ProdutoID { get; set; }
        [DataMember]
        public string ProdutoNome { get; set; }
        [DataMember]
        public string ProdutoQuantidade { get; set; }
        [DataMember]
        public decimal ProdutoPreco { get; set; }
        [DataMember]
        public bool ProdutoAtivo { get; set; }
    }
 

Implementando os contratos de serviço

Para implementar os dois serviços de interface definidos anteriormente vamos começar alterando o nome do arquivo Service1.cs para ProdutoService.cs;

A seguir abra o arquivo ProdutoService.cs e faça o seguinte:

1- Delete os métodos GetData e GetDataUsingDataControls e inclua o seguinte método para obter um produto chamado GetProduto com o seguinte código:

        public Produto GetProduto(int id)
        {
            // TODO: chamar a camada de negócios para retornar o produto
            Produto produto = new Produto();
            produto.ProdutoID = id;
            produto.ProdutoNome ="Produto Teste";
            produto.ProdutoPreco = 10.0m;
            produto.ProdutoQuantidade = "Quantidade Teste";
            return produto;
        } 

Neste método criamos um produto teste e o retornamos ao cliente. Mais tarde vamos remover esse código fixo usado no método e chamar a camada de negócios para obter um produto.

Agora vamos implementar o método AtualizaProduto() incluindo o código abaixo na classe ProdutoService :

        public bool AtualizarProduto(Produto produto,ref string mensagem)
        {
            bool resultado = true;
            // verifica se o preço é válido
            if (produto.ProdutoPreco <= 0)
            {
                mensagem = "O Preço não pode ser menor que zero.";
                resultado = false;
            }
            else if (string.IsNullOrEmpty(produto.ProdutoNome))
            {
                mensagem = "O nome do produto não pode ser vazio";
                resultado = false;
            }
            else if (string.IsNullOrEmpty(produto.ProdutoQuantidade))
            {
                mensagem = "A quantidade não pode ser vazia";
                resultado = false;
            }
            else
            {
                // TODO: chamar a camada de negócios para atualizar o produto
                mensagem = "O produto foi atualizado com sucesso";
                resultado = true;
            }
            return resultado;
        }

Neste método não estamos realmente atualizando um produto pois estamos retornando sempre true se um produto for válido. Mais adiante vamos implementar na lógica de negócios e aplicar ao método AtualizafProduto.

Agora que encerramos os ajustes podemos visualizar o código completo da classe ProdutoService:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace NorthwindService
{
    public class ProdutoService : IProdutoService
    {
        public Produto GetProduto(int id)
        {
            // TODO: chamar a camada de negócios para retornar o produto
            Produto produto = new Produto();
            produto.ProdutoID = id;
            produto.ProdutoNome ="Produto Teste";
            produto.ProdutoPreco = 10.0m;
            produto.ProdutoQuantidade = "Quantidade Teste";
            return produto;
        }
        public bool AtualizarProduto(Produto produto,ref string mensagem)
        {
            bool resultado = true;
            // verifica se o preço é válido
            if (produto.ProdutoPreco <= 0)
            {
                mensagem = "O Preço não pode ser menor que zero.";
                resultado = false;
            }
            else if (string.IsNullOrEmpty(produto.ProdutoNome))
            {
                mensagem = "O nome do produto não pode ser vazio";
                resultado = false;
            }
            else if (string.IsNullOrEmpty(produto.ProdutoQuantidade))
            {
                mensagem = "A quantidade não pode ser vazia";
                resultado = false;
            }
            else
            {
                // TODO: chamar a camada de negócios para atualizar o produto
                mensagem = "O produto foi atualizado com sucesso";
                resultado = true;
            }
            return resultado;
        }
    }
}

Modificando o arquivo App.Config

Como alteramos o nome do serviço vamos ter que fazer algumas alterações no arquivo App.Config. Para isso faça o seguinte:

1- Abra o arquivo App.Config a partir da janela Solution Explorer

2- Altere todas as instâncias de IService1 e Service1 para IProdutoService e ProdutoService.

3- Remova  Design_Time_Addresses do endereço "http://localhost:8733/Design_Time_Addresses/NorthwindService/ProdutoService/" na seção <baseAddresses> ;

Ao final o seu arquivo App.Config deverá possuir o seguinte código:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" />
  </system.web>
  <!-- When deploying the service library project, the content of the config file must be added to the host's 
  app.config file. System.Configuration does not support config files for libraries. -->
  <system.serviceModel>
    <services>
      <service name="NorthwindService.ProdutoService">
        <endpoint address="" binding="basicHttpBinding" contract="NorthwindService.IProdutoService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8733/NorthwindService/ProdutoService/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, 
          set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="True" httpsGetEnabled="True"/>
          <!-- To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Testando o serviço com o cliente WCF Test

Como estamos usando o template WCF Service Library neste exemplo estamos pronto para testar o web service. Este serviço será hospedado no ambiente do Visual Studio WCF Service Host.

Para iniciar o serviço pressione F5 e o WcfSvcHost será iniciado e o cliente WCF Test será iniciando também.

Nota: Você tem que estar logado como administrador e iniciar o Visual Studio como administrador do sistema para poder executar o projetos sem erros.

Você deverá visualizar a janela WCF Test Client conforme mostra a figura a seguir:

Agora, a partir deste cliente podemos clicar duas vezes em uma operação para testá-la. Vamos começar com a operação GetProduto() :

1- Clique duas vezes sobre a operação GetProduto(). Você deverá obter um painel do lado direito conforme mostra a figura abaixo:

Informe um valor inteiro para o Id do Produto na campo Value e clique no botão Invoke para chamar o serviço.

Você deverá uma janela de alerta de segurança sobre enviar informação na rede ser apresentada. Clique no botão OK e para que o serviço seja invocado.

Após a conexão ser estabelecida o canal será criado e o cliente chamará o serviço para realizar a operação requisitada.

A resposta devolvida ao cliente deverá ser apresentada no painel à direita conforme mostra a figura abaixo:

Esta resposta será obtida para qualquer valor informado pois definimos valores fixos na classe ProdutoService.

Vamos testar agora a operação AtualizarProduto.

Clique duas vezes na operação AtualizarProduto e você verá uma nova guia no painel da direita;

Informe valores para as entradas do produto e deixe o parâmetro ProdutoPreco com valor igual a zero e clique no botão Invoke:

Você deverá visualizar como resposta da mensagem o valor 'O produto não pode ser menor que zero' conforme abaixo:

Agora informe um valor para o parâmetro e clique novamente no botão Invoke();

O resultado agora mostra a mensagem que o produto foi atualizado com sucesso:

Além deste modo de visualização podemos exibir os resultados no formato XML. Apenas seleciona a guia XML no base inferior do painel e você verá como resultado um XML bem formatado. A partir deste XML você pode ver que eles são mensagens SOAP :

   

Além disso podemos olhar as configurações definidas clicando duas vezes no arquivo Config no lado esquerdo do painel e o arquivo de configuração será exibido no painel do lado direito.

No resultado você verá os bindings do serviço, os endereços do serviço e o contrato para o serviço.

Se você estiver satisfeito com os resultados pode fechar o cliente de teste WCF e voltar para o Visual Studio. Ao fazer isso você verá que o serviço WCF Service Host será parado. (Note que o serviço não esta hospedado no IIS Express).

Na continuação do arquivo veremos como criar o nosso próprio cliente para teste.

O que era desde o princípio, o que ouvimos, o que vimos com os nossos olhos, o que temos contemplado, e as nossas mãos tocaram da Palavra da vida
(Porque a vida foi manifestada, e nós a vimos, e testificamos dela, e vos anunciamos a vida eterna, que estava com o Pai, e nos foi manifestada);
O que vimos e ouvimos, isso vos anunciamos, para que também tenhais comunhão conosco; e a nossa comunhão é com o Pai, e com seu Filho Jesus Cristo.


1 João 1:1-3

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

  Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter

Referências:


José Carlos Macoratti