Desenvolvimento - C#

WCF - Expondo componente COM+

A plataforma .NET é a tecnologia mais recente da Microsoft para o desenvolvimento de componentes, e é intenção da Microsoft tornar o .NET a evolução do COM.

por Israel Aéce



function doClick(index, numTabs, id) { document.all("tab" + id, index).className = "tab"; for (var i=1; i Faça o download do código.

Em 2002 a Microsoft lançou a plataforma .NET, visando o desenvolvimento de softwares e componentes. Apesar de suas evidentes inovações (como metadados, serialização e versionamento), ela é essencialmente uma tecnologia de componente, assim como o tradicional COM (Component Object Model); a plataforma .NET fornece meios (produtivos) para a construção destes componentes, baseando-se em código gerenciado, podendo escrevê-los sob qualquer linguagem considerada .NET, como as mais populares Visual Basic .NET ou Visual C#.

A plataforma .NET é a tecnologia mais recente da Microsoft para o desenvolvimento de componentes, e é intenção da Microsoft tornar o .NET a evolução do COM. Apesar da plataforma .NET ser consideravelmente mais fácil e simples de trabalhar, ela tem os mesmos problemas que o COM, ou seja, não possui seus próprios serviços de componentes, como gerenciamento de transações e concorrência (sincronização), dependência de plataforma, segurança, chamadas enfileiradas, etc.. Ambas tecnologias recorrem ao COM+ para conseguir resolver grande parte destes desafios. No mundo .NET, podemos fazer a referência ao Assembly System.EnterpriseServices.dll em nosso projeto e, conseqüentemente, criarmos componentes que suportem essas funcionalidades.

O sistema operacional como o Windows XP, Windows Vista ou até mesmo os sistemas operacionais de servidores trazem nativamente uma ferramenta administrativa para o gerenciamento dos componentes que estão hospedados e fazem o uso do COM+. Essa ferramenta, chamada Component Services, permite-nos, de forma visual, catalogar e gerenciar os componentes hospedados dentro de um computador específico, possibilitando a configuração de segurança, transação, mensageria, etc. e, um dos recursos existentes nesta mesma ferramenta (em conjunto com a versão 1.5 do COM+), é a possibilidade de expor o componente via XML Web Service, ou seja, permitir que o mesmo seja acessado através do protocolo HTTP. Uma vez habilitado, ela cria um diretório virtual dentro do IIS, colocando dentro dele um ponto de acesso até o WSDL (Web Service Description Language) com a descrição do serviço. Com isso, qualquer cliente pode adicionar a referência a este serviço e consumí-lo dentro da aplicação através da internet (HTTP + SOAP). A imagem abaixo exibe a tela de configuração para expor um componente COM+ via XML Web Service:

Figura 1 - Expondo o componente COM+ como XML Web Service.

Uma consideração importante a ser feita é com relação a segurança: como o diretório virtual não estará protegido por SSL, então é necessário que todas as opções de segurança estejam desabilitadas. Através da imagem acima podemos notar que no campo SOAP VRoot especificamos o nome do diretório virtual a ser criado no IIS e, quando clicamos no botão OK, as seguintes tarefas são executadas:

  • O diretório virtual com o nome especificado no campo SOAP VRoot é criado dentro do IIS, apontando fisicamente para um diretório com o mesmo nome criado em \Windows\System32\COM\SOAPVRoots\.

  • Dentro deste diretório são criados dois arquivos: Default.aspx e Web.Config. Estes arquivos são utilizados para localizar e configurar o Web Service.

Com o surgimento do WCF, uma plataforma de comunicação unificada, a Microsoft não se esqueceu do legado, ou seja, de componentes grandes e complexos hospedados no COM+ e, possibilita a utilização do WCF para expor esse componente através do HTTP. Ao contrário do que vimos anteriormente, não precisamos recorrer ao Component Services para isso. Junto com o SDK do .NET Framework 3.X, a Microsoft disponibiliza uma ferramenta chamada Microsoft Service Configuration Editor que, dentre todas as funcionalidades disponibilizadas, uma delas é a possibilidade de integração de um componente COM+ a um serviço WCF. Analisaremos essa ferramenta e essa funcionalidade mais tarde, ainda neste artigo.

Para exemplificar este cenário, vamos criar um componente para hospedá-lo dentro do COM+. Para que isso seja possível, você precisa se atentar a algumas regras impostas pela plataforma .NET para que isso seja possível. O primeiro passo é a criação de um projeto do tipo Class Library para dar origem a uma DLL e, conseqüentemente, ser hospedada dentro do COM+. Depois disso, é necessário fazer a referência para o Assembly System.EnterpriseServices.dll. A primeira regra entra em cena neste momento: toda classe (componente) que será hospedada dentro do COM+ precisa, obrigatoriamente, herdar da classe base ServicedComponent.

Para disponibilizar o componente para o mundo COM, primeiramente deve-se desenhar o componente visando facilitar esse processo e, para isso, devemos explicitamente implementar Interfaces nos componentes que serão expostos. Isso é necessário porque componentes COM “não podem conter” os membros diretamente e, sendo assim, devem ter as Interfaces implementadas. Apesar do COM poder gerar a Interface automaticamente, é melhor criarmos isso manualmente, o que permitirá um maior controle sob o componente e suas formas de atualização. Quando os componentes COM geram automaticamente a sua Interface, você não pode fazer nenhuma mudança em sua estrutura pública. Isso se deve porque componentes COM são imutáveis. Se você romper essa regra, o componente deixará de ser invocado.

Com essas primeiras regras, já conseguimos evoluir para a criação do componente. Abaixo são mostradas a Interface e a classe que a implementa. Reparem que nem a classe e nem a Interface nada sabem sobre WCF.

using System;
using System.Runtime.InteropServices;

namespace Library
{
    [Guid("11EF17BF-C276-41ea-AEA1-C371CF7704F1")]
    public interface IUsuario
    {
        bool Adicionar(string nome, string email);
        void Excluir(int id);
    }
}
Imports System
Imports System.Runtime.InteropServices

<Guid("4933034B-89B5-4bd8-B08C-DCEF8CB905D1")> _
Public Interface IUsuario
    Function Adicionar(ByVal nome As String, ByVal email As String) As Boolean
    Sub Excluir(ByVal id As Integer)
End Interface
C# VB.NET

using System;
using System.EnterpriseServices;
using System.Runtime.InteropServices;

namespace Library
{
    [Guid("14671242-00FF-4b3c-8F1C-397155857FB7")]
    [ClassInterface(ClassInterfaceType.None)]
    public class Usuarios : ServicedComponent, IUsuario
    {
        public bool Adicionar(string nome, string email)
        {
            //Adicionar a algum repositório
            return true;
        }

        public void Excluir(int id)
        {
            //Excluir do repositório
        }
    }
}
Imports System.EnterpriseServices
Imports System.Runtime.InteropServices

< _
    Guid("7A7A1421-715F-4eda-BF39-234EED707834"), _
    ClassInterface(ClassInterfaceType.None) _
> _
Public Class Usuarios
    Inherits ServicedComponent
    Implements IUsuario

    Public Function Adicionar(ByVal nome As String, ByVal email As String) As _
        Boolean Implements IUsuario.Adicionar

        "Adicionar a algum repositório
        Return True
    End Function

    Public Sub Excluir(ByVal id As Integer) Implements IUsuario.Excluir
        "Excluir do repositório
    End Sub

End Class
C# VB.NET

A primeira consideração a fazer é com relação a herança da classe Usuarios. Podemos reparar que ela herda diretamente da classe ServicedComponent e, além disso, implementa a Interface IUsuario. Podemos notar que tanto a classe quando a Interface estão decoradas com o atributo GuidAtrribute. Podemos recorrer a esta técnica para especificar o GUID para cada um deles, ao invés de deixar isso a cargo do registro do componente no COM+. Outro atributo que está decorando a classe Usuarios é o ClassInterfaceAttribute. Quando ele está definido como None, o COM não criará uma Interface padrão para a classe. Neste caso você deverá, explicitamente, fornecer uma Interface para ser implementada na classe que será exposta para o mundo COM.

Nota: Por padrão, quando criamos um projeto do tipo Class Library, o arquivo AssemblyInfo possui um atributo chamado ComVisibleAttribute definido como False. Isso evitará que o componente seja exposto via COM e, conseqüentemente, você não conseguirá hospedá-lo dentro do COM+. Para evitar maiores surpresas, certifique-se de que ele esteja com esse atributo definido como True.

Finalmente, para concluir a criação do componente de exemplo, apenas precisamos definir um Strong Name para ele e instalá-lo dentro do Global Assembly Cache - GAC. Depois disso, para efetivamente instalar dentro do COM+, podemos recorrer ao utilitário regsvcs.exe ou através do próprio Component Services que fornece um assistente que nos auxilia neste processo.

Até o presente momento não abordamos nada exclusivamente de WCF. Todo o processo até então já era conhecido desde a versão 1.0 do .NET Framework. A partir daqui, para conseguirmos expor esse componente via WCF, vamos recorrer ao utilitário Microsoft Service Configuration Editor. Indo até o menu File, Integrate e, em seguida, COM+ Application, um assistente é inicializado para nos auxiliar neste processo. Basicamente o que ele fará é nos conduzir desde a seleção do componente COM+ até a criação do diretório virtual dentro do IIS. A imagem abaixo ilustra a tela de seleção do componente a ser exposto:

Figura 2 - Selecionando o componente COM+.

Ao selecionarmos a Interface a ser exposta, o próximo passo consiste na seleção dos métodos que serão disponibilizados através do serviço. A imagem abaixo exibe essa tela de seleção de métodos e podemos notar que são exatamente os métodos que criamos na Interface IUsuario, um pouco mais acima.

Figura 3 - Selecionando os métodos a serem expostos.

Ao avançar para o próximo passo, o utilitário nos dá a opção para selecionarmos qual será o tipo de host utilizado pelo WCF para disponibilizarmos um ponto de acesso. Se optarmos por COM+ hosted (utiliza um processo Dllhost.exe), isso permitirá o acesso via TCP, HTTP ou Named Pipe mas obrigará que o serviço já esteja rodando para responder as requisições; já no modo Web hosted podemos utilizar o IIS como host do serviço, podendo ser ativado somente quando a primeira requisição for feita e, para o nosso exemplo, é esta opção que utilizaremos. Além disso, ainda há a possibilidade de adicionarmos ao host um endpoint para acesso aos metadados do serviço. A imagem abaixo ilustra essas opções comentadas:

Figura 4 - Escolhendo o modelo de host.

Como optamos pelo modelo Web hosted, o próximo passo exige que informemos o nome do diretório virtual existente onde a estrutura necessária para acessar o serviço será armazenada. É importante que você crie este diretório virtual antes de iniciar este assistente. Para o exemplo, foi criado um diretório virtual chamado ServicoDeUsuarios e, como podemos ver, ele está listado na tela abaixo:

Figura 5 - Selecionando o diretório virtual.

Finalmente depois de todos esses passos rigorosamente configurados, dois arquivos são criados dentro do diretório virtual exigido no último passo do assistente. Os arquivos são: Library.Usuarios.svc e Web.Config. O nome do primeiro arquivo é o full-name da classe que criamos no COM+ acrescido da extensão *.svc que, por sua vez, caracteriza um serviço WCF. Se abrirmos este arquivo em qualquer editor de texto, veremos que ele tem uma única linha, que é a diretiva @ServiceHost. Abaixo consta o conteúdo deste arquivo:

<%@ServiceHost
    Factory="System.ServiceModel.ComIntegration.WasHostedComPlusFactory" 
    Service="{14671242-00ff-4b3c-8f1c-397155857fb7},
	{629d362f-d757-4f97-86e6-e1901a522607}" %>
Library.Usuarios.svc

O primeiro ponto de análise é o atributo Factory. Esse atributo especifica o tipo que será a "factory" usada por instanciar a classe responsável para servir de host para o serviço. A classe WasHostedComPlusFactory que está dentro do namespace System.ServiceModel.ComIntegration é utilizada quando a implementação do serviço é um componente hospedado no COM+. Já o atributo Service especifica o tipo do serviço que será exposto. Geralmente colocamos aqui o nome da classe que implementa o contrato mas, como estamos expondo um componente que está no COM+, o valor deste atributo recebe uma informação um pouco diferente da tradicional. Neste caso, o atributo Service está recebendo duas GUIDs onde a primeira representa o GUID do componente (classe Usuarios), e a segunda especifica a GUID da aplicação COM+. É importante que essas GUIDs estejam sincronizadas para que tudo funcione da forma correta.

Ainda temos o arquivo Web.Config que também foi automaticamente gerado. Esse arquivo também sofre ligeiras mudanças em relação a uma configuração tradicional de um serviço exposto sob WCF. Abaixo consta o conteúdo do arquivo gerado (algumas linhas foram omitidas por questões de espaço):

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ComServiceMexBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <comContracts>
      <comContract 
          contract="{11EF17BF-C276-41EA-AEA1-C371CF7704F1}"
          name="IUsuario" 
          namespace="http://tempuri.org/11EF17BF-C276-41EA-AEA1-C371CF7704F1"
          requiresSession="true">
        <exposedMethods>
          <add exposedMethod="Adicionar" />
          <add exposedMethod="Excluir" />
        </exposedMethods>
      </comContract>
    </comContracts>
    <services>
      <service 
        behaviorConfiguration="ComServiceMexBehavior" 
        name="{629D362F-D757-4F97-86E6-E1901A522607}, 
		{14671242-00FF-4B3C-8F1C-397155857FB7}">
        <endpoint 
            address="IUsuario" 
            binding="wsHttpBinding" 
            bindingConfiguration="comNonTransactionalBinding"
            contract="{11EF17BF-C276-41EA-AEA1-C371CF7704F1}" />
        <endpoint 
            address="mex" 
            binding="mexHttpBinding" 
            bindingConfiguration=""
            contract="IMetadataExchange" />
      </service>
    </services>
  </system.serviceModel>
</configuration>
Web.Config

O que chama a atenção no arquivo Web.Config gerado é com relação ao nome do serviço exposto e, em especial, ao contrato que é exposto através do endpoint. Da mesma forma que o arquivo Library.Usuarios.svc, o Web.Config não faz referência diretamente ao nome da classe ou da Interface, mas sim aos GUIDs que estão relacionados às mesmas. O serviço exposto é configurado com o GUID que representa a aplicação dentro do COM+ em conjunto com o GUID que representa o componente (a classe Usuarios). Da mesma forma, o contrato exposto não é o nome da Interface que criamos (IUsuario), mas sim o GUID que está relacionado a ela.

Para finalizar, o arquivo Web.Config ainda conta com um novo elemento chamado comContracts que é usado para a integração entre o WCF e os componentes hospedados no COM+. Basicamente esta seção permite configurarmos algumas propriedades (como namespace, sessions, etc.) que, a princípio, deveriam estar no componente mas, como ele não foi desenhado para WCF, você tem a chance de customizar isso neste arquivo.

Depois de todo esse processo, o que precisamos neste momento é apenas fazer a referência deste serviço em nossas aplicações cliente e, via proxy, invocarmos o serviço como se fosse um tradicional serviço WCF. Para efeitos de testes, podemos acessar o endereço HTTP através do navegador e, se tudo correr bem, você deverá visualizar uma página (como mostrado abaixo) com o endereço até o WSDL do serviço.

Figura 6 - Efetuando o teste do serviço.

Conclusão: Como podemos notar no decorrer deste serviço, apesar da Microsoft ter criado uma unificada e consistente plataforma de comunicação, ela não se esqueceu das várias aplicações que foram construídas sob tecnologias anteriores, como é o caso dos XML Web Services e componentes hospedados dentro do COM+. Para aqueles que já possuem o componente, basta recorrer ao utilitário Microsoft Service Configuration Editor para configurar como o mesmo será exposto e, finalmente, desfrutar de todo o poder e flexibilidade que o WCF proporciona.
Israel Aéce

Israel Aéce - Especialista em tecnologias de desenvolvimento Microsoft, atua como desenvolvedor de aplicações para o mercado financeiro utilizando a plataforma .NET. Como instrutor Microsoft, leciona sobre o desenvolvimento de aplicações .NET. É palestrante em diversos eventos Microsoft no Brasil e autor de diversos artigos que podem ser lidos a partir de seu site http://www.israelaece.com/. Possui as seguintes credenciais: MVP (Connected System Developer), MCP, MCAD, MCTS (Web, Windows, Distributed, ASP.NET 3.5, ADO.NET 3.5, Windows Forms 3.5 e WCF), MCPD (Web, Windows, Enterprise, ASP.NET 3.5 e Windows 3.5) e MCT.