Criando serviços WCF com Spring.NET

Publicado por Fabio A. Falavinha
12/1/2011
Categoria:
Tags: , ,

Em uma arquitetura de software bem definida, a exposição da lógica de negócio é feita através da implementação da camada de Serviços (Service Layer).

O objetivo desta camada é assegurar, de forma coerente e robusta, as chamadas de outras partes do sistema, expondo suas operações (métodos) através de uma interface, que define o contrato com as aplicações clientes que farão as chamadas ao serviço de diversas formas de comunicação (diferentes protocolos. Ex.: TCP, SOAP).

A tecnologia WCF (Windows Communication Foundation) é uma das implementações que a Microsoft disponibiliza para construir serviços de forma transparente, através da configuração de múltiplos canais de comunicação ao serviço. A configuração de serviço WCF é simples, porém sua criação pode não ser fácil, pois um serviço pode conter múltiplas dependências, como: outros serviços, objetos de acesso a dados (DAO – Data Access Objet).

Portanto, como garantir uma configuração do serviço WCF e suas dependências?

O uso do pattern Dependeny Injection e do conceito de arquitetura Inversion of Control (IoC) faz com que a construção em tempo de desenvolvimento e execução seja um trabalho mais simples.

O framework Spring.NET disponibiliza um container IoC para configurar todas as dependências do serviço WCF através da configuração XML.

Contruindo o Serviço

Para exemplificar a ideia acima, criarei um exemplo com uma arquitetura cliente/servidor, no qual terei um projeto chamado Server, onde estarão as classes do serviço e suas dependências. E outro projeto chamado Client, onde estarão as classes para realizar a chamada do serviço. Para a implementação do serviço será utilizada a tecnologia WCF versão 3.5, juntamente com o framework Spring.NET versão 1.3.

No projeto Server, criarei o serviço IDummyService e sua implementação DummyService, com uma dependência à classe IDummyDAO, que terá a implementação DummyDAO:

namespace Server.WCF
{
    [ServiceContract]
    public interface IDummyService
    {
        [OperationContract]
        void SayHello(string who);
    }

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class DummyService : IDummyService
    {
        public IDummyDAO DummyDAO { get; set; }

        public void SayHello(string who)
        {
            Console.WriteLine(string.Format("Hello, {0}", who));
            DummyDAO.MakePersistence(who);
        }
    }
}
namespace Server.DAO.Impl
{
    public class DummyDAO : IDummyDAO
    {
        public void MakePersistence(string who)
        {
            Console.WriteLine(who + " saved!");
        }
    }
}
namespace Server.DAO
{
    public interface IDummyDAO
    {
        void MakePersistence(string who);
    }
}

Após a construção, vou configurar o serviço WCF no arquivo App.Config da seguinte forma:

<configuration>
  <appSettings />
  <system.serviceModel>
    <bindings>
      <netTcpBinding>
        <binding name="server-binding" transferMode="Buffered">
          <security mode="None" />
        </binding>
      </netTcpBinding>
    </bindings>
    <services>
      <service name="dummyService">
        <endpoint address="net.tcp://localhost:8732/dummy/tcp" binding="netTcpBinding"
          bindingConfiguration="server-binding" name="tcp" contract="Server.WCF.IDummyService" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

Até o momento, a implementação do serviço WCF foi realizada, porém a configuração de sua dependência com o recurso IDummyDAO não foi disponibilizada. Com a utilização do framework Spring.NET a configuração desta dependência pode ser feita da seguinte forma:

<objects xmlns="http://www.springframework.net">

  <object id="dummyService" singleton="false" type="Server.WCF.DummyService, Server">
    <property name="DummyDAO" ref="dummyDAO" />
  </object>

  <object id="dummyDAO" type="Server.DAO.Impl.DummyDAO, Server" />

  <object id="dummyServiceHost" type="Spring.ServiceModel.Activation.ServiceHostFactoryObject, Spring.Services">
    <property name="TargetName" value="dummyService" />
  </object>

</objects>

Note, que a criaçao do serviço é feita através do elemento dummyServiceHost, que será disponibilizado através da factory (Spring.ServiceModel.Activation.ServiceHostFactoryObject), que criará objetos do tipo SpringServiceHost (implementação do framework Spring.NET), respeitando a interface ServiceHost da tecnologia WCF. A configuração básica do serviço é realizada através do elemento dummyService, definido como singleton=false para interagir de forma correta com a tecnologia WCF, no qual sua dependência é configurada pela propriedade DummyDAO, referenciada pelo elemento dummyDAO (veja o código da classe DummyService acima). Para habilitar esta configuração, basta adicionar ao arquivo App.Config da aplicação o framework Spring.NET da seguinte forma:

<configuration>
  <configSections>
    <sectionGroup name="spring">
      <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
    </sectionGroup>
  </configSections>
  <appSettings />
  <spring>
    <context>
      <resource uri="assembly://Server/Server/Server-Context.xml" />
    </context>
  </spring>
  <system.serviceModel>
    <bindings>
      <netTcpBinding>
        <binding name="server-binding" transferMode="Buffered">
          <security mode="None" />
        </binding>
      </netTcpBinding>
    </bindings>
    <services>
      <service name="dummyService">
        <endpoint address="net.tcp://localhost:8732/dummy/tcp" binding="netTcpBinding"
          bindingConfiguration="server-binding" name="tcp" contract="Server.WCF.IDummyService" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

Chamando o Serviço

Após a construção e configuração do serviço, vou adicionar ao projeto Client a configuração de chamada do serviço WCF via Spring.NET. Portanto, vou criar o arquivo Client-Context.xml que terá a configuração para chamada do serviço WCF:


<objects xmlns="http://www.springframework.net">

  <object id="dummyServiceClient" type="Server.WCF.IDummyService, Server"
          factory-object="serverAppChannelFactory" factory-method="CreateChannel" />

  <object id="serverAppChannelFactory"
          type="System.ServiceModel.ChannelFactory<Server.WCF.IDummyService>, System.ServiceModel">
    <constructor-arg name="endpointConfigurationName"
                     value="serverAppDummyServiceEndpoint" />
  </object>

</objects>

O elemento dummyServiceClient define o proxy a ser criado pela factory serverAppChannelFactory, definida pela classe ChannelFactory da tecnologia WCF, no qual referencia o endpoint configurado no arquivo App.Config.

A configuração acima pode ser entendida conforme o código de chamada de um serviço WCF:

  return new ChannelFactory<IDummyService>("serverAppDummyServiceEndpoint").CreateChannel();

No arquivo App.Config do projeto Client, vou adicionar o endpoint (endereço de onde está publicado o serviço) e a configuração do framework Spring.NET da mesma forma que fiz no projeto Server, apontando para o arquivo de contexto do cliente:

<configuration>
  <configSections>
    <sectionGroup name="spring">
      <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
    </sectionGroup>
  </configSections>
  <appSettings />
  <spring>
    <context>
      <resource uri="assembly://Client/Client/Client-Context.xml" />
    </context>
  </spring>
  <system.serviceModel>
    <bindings>
      <netTcpBinding>
        <binding name="server-binding" transferMode="Buffered">
          <security mode="None"/>
        </binding>
      </netTcpBinding>
    </bindings>
    <client>
      <endpoint address="net.tcp://localhost:8732/dummy/tcp"
                binding="netTcpBinding" bindingConfiguration="server-binding"
                contract="Server.WCF.IDummyService" name="serverAppDummyServiceEndpoint"/>
    </client>
  </system.serviceModel>
</configuration>

Para este exemplo, o projeto Client foi criado como Console Application, no qual utilizarei a classe Program para realizar a chamada ao serviço via Spring:

    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                IApplicationContext context = ContextRegistry.GetContext();
                IDummyService dummyService = (IDummyService)context["dummyServiceClient"];
                dummyService.SayHello("User (" + DateTime.Now.Ticks + ")");
            }
            catch (Exception cause)
            {
                Console.WriteLine(cause);
            }
        }
    }

A partir do contexto criado pelo framework Spring, recupero o objeto dummyServiceClient, que respeita a interface IDummyService, para fazer chamadas ao serviço remoto.

Conclusão

Construir e configurar serviços com o framework Spring.NET é uma tarefa simples. O uso de outros paradigmas como AOP (Aspect Oriented-Programming) a serviços WCF podem ser úteis para assegurar o ciclo de vida das operações a cada chamada. Porém, alguns cuidados devem ser tomados:

  1. Ao usar AOP com Spring.NET aos serviços WCF faz com que os objetos recebam uma camada de proxy a mais do que a criada pela tecnologia WCF, ocasionando em overhead de chamadas no ciclo de uma operação, ou seja, perda de performance da aplicação.
  2. A configuração de serviços WCF, de forma programática, via Spring é limitada apenas a configuração via arquivo XML, ou seja, não se pode criar serviços singleton utilizando a classe SpringServiceHost, pois objetos associados ao framework Spring já são desta natureza, o que causa um problema na interação entre o framework com a tecnologia WCF.

Referências





Desenvolvido por hacklab/ com WordPress