Olá pessoal, meu nome é Rafael e este é meu primeiro artigo aqui no .NET Coders. Hoje eu vou falar sobre WCF hospedado em um serviço gerenciado do Windows.

Como muitos já sabem, o WCF é um Framework desenvolvido e mantido pela Microsoft que permite a construção de aplicações orientadas a serviços. Ele é muito utilizado em ambientes de aplicações com Web que normalmente rodam em cima de um servidor IIS, mas o que muita gente não sabe é que o WCF não necessariamente precisa do IIS para funcionar.

Por que utilizar o WCF?

  • Interoperabilidade
  • Segurança
  • Alta Disponibilidade
  • Múltiplos Protocolos de Comunicação Suportados (HTTP, TCP, MSMQ e outros)
  • Suporta Chamadas SOAP e REST

Tipos de Hosting disponíveis:

  • IIS
  • IIS + WAS
  • Self Hosting (Console Application, Windows Service…)

Percebam que eu destaquei o últimos item da lista acima. Isto é porque a configuração dos dois tipos de Hosting são bem similares. A diferença entre o Self Hosting e o Windows Service Hosting é que a segunda opção aproveita das vantagens de gerenciamento e infra-estrutura de um serviço do Windows. Um WCF Self Hosting pode rodar em cima de um Console Application, WPF ou Windows Forms.

Sendo assim, é possível utilizar o WCF para fazer a interoperabilidade entre aplicações que não sejam necessariamente aplicações Web. Abaixo estarei explicando passo a passo como disponibilizar um serviço WCF em um projeto Windows Services.

Criação do Projeto

Primeiramente, devemos criar um projeto do tipo “Windows Services”.

Seguindo o caminho abaixo, vamos à criação do Projeto:

File > New > Project > Visual C# > Windows > Windows Services > OK

Criei o projeto na versão 4.5 do Framework, mas você pode usar a versão 4.5.1 ou 4.

Figura 1: Criação do Projeto.

Imagem 1: Criação do Projeto.

Referências do Projeto

Pic1

Imagem 2: Referências do Projeto.

Percebam que foram adicionados as seguintes referências:

  • System.ServiceModel
  • System.ServiceModel.Web
  • System.ServiceProcess

Elas são importantes porque fazem parte do modelo de serviço do WCF.

Estrutura do Projeto

Depois disto, crie a seguinte estrutura de diretórios no seu projeto de Windows Services:

Imagem 3: Estrutura do Projeto.

Imagem 3: Estrutura do Projeto.

Criação do DTO

Criaremos agora um DTO fictício apenas para ilustrar o nosso artigo. Ele é somente uma classe POCO e bem simples. Na pasta “DTO” que criamos na estrutura do projeto, adicione a classe “Pessoa.cs”. Veja o código abaixo:

namespace WcfSelfHosted.DTO
{
    public class Pessoa
    {
        public string Nome { get; set; }

        public int Idade { get; set; }
    }
}

Interface-Contrato do Serviço

Uma vez que todo serviço WCF funciona com um contrato via Interface, vamos agora criar a Interface do nosso serviço de exemplo. Crie na pasta “Interfaces” a Interface “IServicoExemplo.cs”. Veja o código dela abaixo:

using WcfSelfHosted.DTO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.Threading.Tasks;

namespace WcfSelfHosted.Interfaces
{
    [ServiceContract]
    public interface IServicoExemplo
    {
        [OperationContract]
        [WebGet]
        Pessoa GetPessoaById(int Id);
    }
}

O atributo “ServiceContract” indica que esta é uma Interface de um serviço WCF, e o atributo “OperationContract” indica que este é um método que deverá ser exposto no serviço.

O atributo “WebGet” foi adicionado para permitir que esta operação seja chamada via REST.

Implementação concreta do Serviço

Com nosso contrato criado, vamos agora implementar as funcionalidades do serviço. Para isto, crie na pasta “Services” a classe “ServicoExemplo.cs”. Veja o código da classe abaixo:

using WcfSelfHosted.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WcfSelfHosted.Services
{
    public class ServicoExemplo : IServicoExemplo
    {
        public DTO.Pessoa GetPessoaById(int Id)
        {
            return new DTO.Pessoa()
            {
                Nome = "Rafael Paschoal de Carvalho",
                Idade = 23
            };
        }
    }
}

Veja que a classe implementa o contrato que acabamos de criar (IServicoExemplo). No método “GetPessoaById” estou retornando uma instância do DTO Pessoa somente a fim de elucidar melhor o artigo, uma vez que em um cenário real teríamos regras de negócio, acesso a banco, validações e outros recursos implementados.

Ciclo de vida do Host e Inicialização

Agora que já temos o serviço implementado em cima de um contrato definido e já preparado nos padrões do WCF, é necessário criar o Host do WCF que irá funcionar dentro de nosso Windows Service, e adicionar nosso respectivo serviço. O código abaixo é a classe que todo serviço do Windows tem e que implementa a classe “ServiceBase”. Veja o código abaixo:

using WcfSelfHosted.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;

namespace WcfSelfHosted
{
    public class WcfSelfHosted : ServiceBase
    {
        /// <summary>
        /// O host onde o serviço WCF ficará hosteado.
        /// </summary>
        protected IList<ServiceHost> ServiceHost { get; set; }

        #region Windows Service => Ciclo de Vida

        public WcfSelfHosted()
        {
            // O nome do Windows Service
            ServiceName = "WcfSelfHosted";
        }

        public static void Main()
        {
            ServiceBase.Run(new WcfSelfHosted());
        }

        /// <summary>
        /// Quando o módulo é inicializado.
        /// </summary>
        /// <param name="args">Os argumentos de inicialização.</param>
        protected override void OnStart(string[] args)
        {
            System.Globalization.CultureInfo.DefaultThreadCurrentCulture = new System.Globalization.CultureInfo("pt-BR");
            System.Globalization.CultureInfo.DefaultThreadCurrentUICulture = new System.Globalization.CultureInfo("pt-BR");

            ServiceHost = new List<ServiceHost>();
            ConfigurarServicos();
        }

        /// <summary>
        /// Quando o módulo gerenciável é parado.
        /// </summary>
        protected override void OnStop()
        {
            FecharServiceHosts();
            DisposeServicesHosts();
        }

        #endregion

        #region METODOS UTILITÁRIOS DE HOST PARA WCF

        /// <summary>
        /// Inicializa o listener do WCF.
        /// </summary>
        protected void InicializarServiceHosts()
        {
            foreach (var sv in ServiceHost)
            {
                try
                {
                    EventLog.WriteEntry(this.ServiceName, "Abrindo serviço do WCF (Rest e Soap / HTTP) => " + sv.BaseAddresses[0], EventLogEntryType.Information);
                    sv.Open();
                    EventLog.WriteEntry(this.ServiceName, "Serviço WCF em funcionamento (Rest e Soap / HTTP) => " + sv.BaseAddresses[0], EventLogEntryType.Information);
                }
                catch (Exception ex)
                {
                    EventLog.WriteEntry(this.ServiceName, "Erro ao abrir serviço WCF (Rest e Soap / HTTP) => " + ex.Message, EventLogEntryType.Error);
                }
            }
        }

        /// <summary>
        /// Inicializa o WCF para chamadas síncronas de rotinas do módulo Bal.
        /// </summary>
        protected void FecharServiceHosts()
        {
            foreach (var sv in ServiceHost)
            {
                if (sv != null)
                {
                    try
                    {
                        sv.Close();
                        EventLog.WriteEntry(this.ServiceName, "Serviço WCF encerrado (Rest e Soap / HTTP) => " + sv.BaseAddresses[0], EventLogEntryType.Information);
                    }
                    catch (Exception ex)
                    {
                        EventLog.WriteEntry(this.ServiceName, "Erro ao encerrar serviço WCF (Rest e Soap / HTTP) => " + ex.Message, EventLogEntryType.Error);
                    }
                }
            }
        }

        /// <summary>
        /// Dispose nos objetos de service host do WCF.
        /// </summary>
        protected void DisposeServicesHosts()
        {
            foreach (var sv in ServiceHost)
            {
                if (sv != null)
                {
                    ((IDisposable)sv).Dispose();
                }
            }

            EventLog.WriteEntry(this.ServiceName, "Serviços WCF dispostos/removidos (Rest e Soap / HTTP).", EventLogEntryType.Information);
        }

        /// <summary>
        /// Registra e configura os serviços WCF para um módulo da aplicação.
        /// </summary>
        private void ConfigurarServicos()
        {
            // Adicionar uma linha desta com cada type de novo serviço criado na pasta "Services"
            ServiceHost.Add(new ServiceHost(typeof(ServicoExemplo)));

            InicializarServiceHosts();
        }

        #endregion
    }
}

Explicando o código:

No ínicio da classe criamos uma propriedade que irá conter nossas instâncias da classe “ServiceHost”, que é a classe responsável por fazer o hosting de serviços WCF e que fica no namespace System.ServiceModel da .NET Framework. Mais em baixo, dividido em uma região está nosso ciclo de vida do serviço do Windows. Perceba que no evento OnStart é chamado a criação dos serviços de hosting, e no evento OnStop fechamos e efetivamos a disposição destas respectivas instâncias.

Já no segundo “region” mais abaixo, temos o código que efetivamente faz o hosting do WCF. No método “ConfigurarServicos” temos a seguinte linha:

ServiceHost.Add(new ServiceHost(typeof(ServicoExemplo)));

Para cada serviço novo que desejarmos expor, devemos adicionar uma linha desta para que ele funcione e seja exposto. Esta tarefa pode ser feita via Reflection percorrendo todos os itens deste assembly, mas não é o foco deste artigo então vamos fazer desta forma “mecânica” mesmo. Fica a dica para quem quiser implementar esta solução em um ambiente real, e se tiverem alguma dúvida de como fazer isto via Reflection, fiquem a vontade para entrar em contato comigo.

Arquivo de Configuração

Como última etapa de configuração do nosso ambiente WCF, devemos definir algumas configurações no arquivo App.config do nosso projeto. Siga as configurações conforme exemplificado abaixo (Reparem que eu coloquei apenas o nó system.ServiceModel. O arquivo App.Config contém outras configurações, mas que não estão relacionadas com a configuração do WCF tratadas neste artigo):

  <system.serviceModel>
    <services>
      <service behaviorConfiguration="ServiceBehavior" name="WcfSelfHosted.Services.ServicoExemplo">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8000/WcfSelfHosted/ServicoExemplo"/>
          </baseAddresses>
        </host>
        <endpoint address="soap" binding="basicHttpBinding" contract="WcfSelfHosted.Interfaces.IServicoExemplo" bindingConfiguration="LargeSoap"/>
        <endpoint address="rest" binding="webHttpBinding"  behaviorConfiguration="jsonBehavior" contract="WcfSelfHosted.Interfaces.IServicoExemplo" bindingConfiguration="LargeRest"/>
      </service>
    </services>

    <behaviors>
      <serviceBehaviors>
        <behavior name="ServiceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="False"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="jsonBehavior">
          <enableWebScript/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <bindings>
      <basicHttpBinding>
        <binding name="LargeSoap" allowCookies="true"
                 maxReceivedMessageSize="1500000"
                 maxBufferSize="1500000"
                 maxBufferPoolSize="1500000">
          <readerQuotas maxDepth="32"
               maxArrayLength="656000"
               maxStringContentLength="656000"/>
        </binding>
      </basicHttpBinding>

      <webHttpBinding>
        <binding name="LargeRest"
                 maxBufferPoolSize="1500000"
                 maxReceivedMessageSize="1500000"
                 maxBufferSize="1500000">
          <readerQuotas
                maxArrayLength="656000"
                maxBytesPerRead="656000"
                maxDepth="32"
                maxNameTableCharCount="656000"
                maxStringContentLength="656000"
            />
        </binding>
      </webHttpBinding>
    </bindings>
  </system.serviceModel>

Dentro do nó <system.serviceModel> é onde definimos as configurações do WCF, e para quem já tem experiência com WCF no IIS, estas configurações são semelhantes.

Dentro de <services> foi configurado nosso serviço e respectivo endereço URI. Também foram configurados dois Endpoints, um SOAP e outro REST, significando que este serviço poderá ser acessado via protocolo SOAP ou REST. É possível implementar outros endpoints também, como o netTcpBinding que possui um overhead menor do que o HTTP e permite um throughput maior no canal, mas isto varia do cenário de cada aplicação e como serão utilizados estes serviços a fim de interoperabilidade. Como na maioria dos cenários são utilizados SOAP ou REST, vamos manter somente estes dois endpoints no artigo.

Dentro de <behaviors> configuramos alguns comportamentos para nosso serviço. Habilitamos também o retorno de JSON para nosso endpoint REST.

Dentro de <bindings> fizemos configurações dos canais para permitir objetos maiores e mais robustos, já atendendo qualquer cenário de larga escala. O padrão do WCF é meio tímido, portanto precisamos aumentar este limite se quisermos trabalhar com retorno de grande coleções de dados e objetos com um número razoável de propriedades. Deixaremos esta configuração assim porque ela atende tanto micro cenários quanto macros.

Com todas estas configurações efetivadas, já poderíamos acessar nosso serviço WCF através da URL http://localhost:8000/WcfSelfHosted/ServicoExemplo. Como o escopo do artigo não engloba a instalação de um serviço do Windows, eu não vou entrar neste mérito, mas nossa aplicação já está pronta para receber requisições SOAP e REST.

Conclusão

Bom pessoal, chegamos ao fim do artigo e com isto podemos perceber que o WCF é um Framework muito robusto e que atende diversos cenários de interoperabilidade entre aplicações, e que não estamos limitados somente ao IIS.

Espero que tenham gostado, e qualquer opinião, dica ou crítica é válida. Um grande abraço a todos!

Desenvolvedor .NET há mais de 5 anos, apaixonado por Arquitetura e Desenvolvimento de Software.

Atuo hoje como Senior Software Developer em [email protected] Zelândia, mas ao longo da minha carreira profissional trabalhei como Analista de Sistemas e Desenvolvedor em algumas empresas brasileiras e em vários projetos de Software!

Microsoft Certified Professional
MS: Programming in HTML5 with JavaScript and CSS3
MCTS: Web Applications Development with Microsoft .NET Framework 4

Website: http://rafaelpc.com/
LinkedIn: https://br.linkedin.com/in/rafaelpaschoal

Comentários

comentarios