WCF - MessageContracts

by Israel Aece 24. May 2009 16:32

Quando desenvolvemos serviços WCF geralmente iniciamos com a criação do contrato, suas respectivas operações e possíveis parâmetros que elas aceitam e/ou retornam. Os tipos que são enviados e recebidos por um serviço podem ser desde um tipo simples, como um DateTime, Integer ou String até mesmo tipos mais complexos, como classes que representam alguma estrutura específica da nossa aplicação.

Esses dados (simples ou complexos) que definimos no contrato serão sempre serializados e enviados como parte do body da mensagem SOAP, ficando sob responsabilidade do WCF, montar a mensagem (headers e body) de acordo com as informações que serão enviadas ao destino (body) e como o binding será configurado (headers). Mas poderemos ter situações em que esse comportamento padrão não nos atenderá, e é neste momento que entra em cena os contratos de mensagem, ou Message Contracts.

Através dos contratos de mensagem, podemos ter o controle total da estrutura da mensagem SOAP, customizando como e onde os dados que fazem parte do contrato serão acomodados dentro da mesma, já que podem fazer parte do header ou do body. Em algumas situações você precisará dessa customização, como por exemplo, quando você necessitar de uma melhor interoperabilidade com um determinado cliente, que possivelmente poderá exigir a mensagem em um formato diferente do qual o WCF cria. Além disso, você poderá ter um objeto não fará somente parte do body, mas também há propriedades dentro dele que deverão ser inseridos como elementos na coleção de headers.

Quando formos trabalhar com contratos de mensagem, além dos contratos que somos obrigados a criar em qualquer tipo de serviço (interface que representa o contrato do serviço e as classes que serão utilizadas como contratos de dados), temos também que criar as classes que representarão o contrato (formato) da mensagem que será enviada/recebida pela operação. Nesse contrato, basicamente temos propriedades que expõem tipos (complexos ou não) e que serão definidas como parte dos headers ou do body da mensagem.

Em primeiro lugar, precisamos definir que a classe será utilizada como contrato de mensagem, e para isso, decoramos ela com o atributo chamado MessageContractAttribute. É importante dizer que as classes que servem como contrato de mensagem devem, obrigatoriamente, ter um construtor sem parâmetros. Em seguida, para determinar se um campo desta classe vai fazer parte dos headers ou body, você deverá utilizar os atributos MessageHeaderAttribute ou MessageBodyMemberAttribute, respectivamente.

Para exemplificar o uso de contratos de mensagem, vamos imaginar a seguinte situação: uma aplicação poderá enviar para o serviço a relação de contas de um determinado cliente que ela quer recuperar os respectivos saldos. O serviço, por sua vez, extrairá de algum repositório o saldo atual de cada uma das contas, e retornará para o solicitante. Neste caso, teremos duas classes: CriterioDeBusca e ResultadoDaBusca.

A primeira delas representará a mensagem que o cliente irá enviar para o serviço. Essa classe disponibiliza três campos: Cliente, Codigo e Contas, utilizadas para a aplicação informar o cliente e as contas que ele deseja extrair o saldo. Já a classe ResultadoDaBusca irá representar o resultado da pesquisa, com os campos Cliente, Codigo e Saldos. A última propriedade desta classe retorna um array, onde cada elemento é representa por um objeto do tipo Saldo, que é composto pela identificação da conta e seu respectivo saldo. Abaixo temos a estrutura de cada uma delas:

[MessageContract]
public class CriterioDeBusca
{
    [MessageHeader] public string Cliente;
    [MessageHeader] public int Codigo;
    [MessageBodyMember] public string[] Contas;
}

[MessageContract(IsWrapped = true, WrapperName = "contas")]
public class ResultadoDaBusca
{
    [MessageHeader] public string Cliente;
    [MessageHeader] public int Codigo;
    [MessageBodyMember] public Saldo[] Saldos;
}

public class Saldo
{
    public string Conta { get; set; }
    public decimal Valor { get; set; }
}

Como podemos notar, as classes CriterioDeBusca e ResultadoDaBusca estão decoradas com o atributo MessageContractAttribute, que diz ao runtime do WCF que elas devem ser consideradas como a mensagem SOAP em si, e não como simples contratos de dados. Os campos destas classes também possuem os atributos para determinar o que irá compor o header e o body.

Já comentamos a finalidade de cada atributo que foi decorado nos membros acima. Apesar de eles estarem com a configuração padrão (com exceção da classe ResultadoDaBusca), ainda há uma série de propriedades que cada um deles disponibiliza, para que possamos definir algumas outras configurações que serão utilizadas pelo WCF durante o processamento/serialização da mensagem. Antes de prosseguirmos, é necessário entender cada uma dessas propriedades que podemos, opcionalmente, utilizar. As tabelas abaixo sumarizam cada uma delas.

MessageContractAttribute
Propriedade Descrição
HasProtectionLevel Recebe um valor boleano indicando se a mensagem deverá ter um nível de proteção.
IsWrapped Recebe um valor boleano indicando se o corpo da mensagem terá um elemento que servirá como wrapper. Caso True, o WCF utilizará as informações definidas nas propriedades WrapperName e WrapperNamespace para criá-lo.
ProtectionLevel Propriedade que recebe um dos valores definidos pelo enumerador ProtectionLevel, indicando se a mensagem deverá ser encriptada, assinada, ambos ou não precisará de nenhuma espécie de proteção.
WrapperName Define o elemento que servirá como wrapper para o body da mensagem. Quando omitido ou quando a propriedade IsWrapped estiver definida como False, o corpo será colocado imediatamente após o elemento <soap:Body>.
WrapperNamespace Define o namespace para o elemento que servirá como wrapper da mensagem.

MessageHeaderAttribute
Propriedade Descrição
HasProtectionLevel Recebe um valor boleano indicando se a mensagem deverá ter um nível de proteção.
Name Define o nome do elemento que será serializado. Quando omitido utilizará o nome do próprio campo.
Namespace Fornece um namespace para o header em questão e seus possíveis filhos, a menos que eles sobrescrevam isso.
ProtectionLevel Propriedade que recebe um dos valores definidos pelo enumerador ProtectionLevel, indicando se a mensagem deverá ser encriptada, assinada, ambos ou não precisará de nenhuma espécie de proteção.
Actor Define uma URI que determina a quem se destina aquele atributo.
MustUnderstand Valor boleano que indica se o ator a quem se destina aquela informação deverá ou não entendê-la. Caso esteja definido como True, e o ator não entende aquele header, uma fault deverá ser lançada.
Relay Também define um valor boleano, que indica se o header deverá ser encaminhado para o próximo nó, caso ele não seja interpretado pelo ator atual.

MessageBodyMemberAttribute
Propriedade Descrição
HasProtectionLevel Recebe um valor boleano indicando se a mensagem deverá ter um nível de proteção.
Name Define o nome do elemento que será serializado. Quando omitido utilizará o nome do próprio campo.
Namespace Fornece um namespace para o header em questões e seus possíveis filhos, a menos que eles sobrescrevam isso.
Order  Como o próprio nome diz, é uma propriedade que recebe um valor inteiro indicando a ordem de serialização de cada elemento dentro do corpo da mensagem. Quando omitido, os elementos são serializados de forma alfabética, seguido dos campos que tem essa propriedade definida explicitamente.
ProtectionLevel  Propriedade que recebe um dos valores definidos pelo enumerador ProtectionLevel, indicando se a mensagem deverá ser encriptada, assinada, ambos ou não precisará de nenhuma espécie de proteção.

Ainda temos uma especialização da classe MessageHeaderAttribute, que é a MessageHeaderArrayAttribute. Este atributo pode ser somente aplicado a membros do tipo array e que serão acomodados no header da mensagem. Decorando uma propriedade ou campo com este atributo, os elementos do array serão serializados de forma independente, ao invés de estarem envolvidos por um wrapper. Por exemplo, se tivermos um array de saldos, por padrão, ele será serializado da seguinte forma:

<header value="1" />
<saldos>
  <conta numero="123" valor="100.00" />
  <conta numero="456" valor="200.00" />
</saldos>
<header value="2" />

Já quando aplicamos o atributo MessageHeaderArrayAttribute, ele ficará da seguinte forma:

<header value="1" />
<conta numero="123" valor="100.00" />
<conta numero="456" valor="200.00" />
<header value="2" />

Depois disso, o próximo passo é a criação do contrato. Agora, ao invés de trabalharmos diretamente com os tipos mais simples e/ou complexos nos parâmetros e resultado das operações, elas devem começar a trabalhar diretamente com as classes previamente criadas. As classes que criamos para o exemplo são autoexplicativas, onde uma representa a entrada de dados e a outra o resultado, e como ambas estão decoradas como o atributo MessageContractAttribute, elas definirão a estrutura da mensagem SOAP. Abaixo está o contrato já configurado com elas:

[ServiceContract]
public interface IContrato
{
    [OperationContract]
    ResultadoDaBusca RecuperarSaldo(CriterioDeBusca criterio);
}

Observação: Você ainda tem uma segunda alternativa para a definação de contratos de mensagem, que também é conhecida como “inline”. Neste modo, você pode decorar os parâmetros do teu contrato com os atributos MessageHeaderAttribute ou MessageBodyAttribute.

RPC vs. Messaging

Uma das idéias das aplicações orientadas a serviços é que elas devem trocar mensagens, ou seja, ao invocar uma operação, teoricamente, você deveria criar uma mensagem explicitamente e enviá-la para o seu destino, enquanto do outro lado, você deve capturá-la, analisar o seu conteúdo, e executar a tarefa que ela solicita. O contrato que vimos acima segue essa linha, já que devemos lidar com objetos que representam a mensagem. Esse tipo de formato é conhecido como Messaging.

Existe também uma outra possibilidade, conhecida como RPC (Remote Procedure Call). Neste estilo, nós trabalhamos com os serviços como se fossem componentes/classes tradicionais, ou seja, não precisamos lidar com as mensagens diretamente, pois o método irá receber e/ou retornar os tipos corretos e a tecnologia, que neste caso é o WCF, se encarregará de montar e serializar a mensagem que será enviada de um lado a outro.

O WCF não está limitado a apenas um desses estilos de mensagens, mas na maioria das vezes utilizamos o estilo RPC. Mas o tema deste artigo é justamente mostrar como podemos desenvolver serviços que utilizem o padrão Messaging, e como já vimos acima, vamos trabalhar diretamente com as classes que representarão as mensagens.

Implementação

Ambos os lados da comunicação precisarão trabalhar de forma diferente, já que não vamos mais passar ou receber os parâmetros diretamente; agora vamos lidar com uma classe que representará a resposta e uma classe que representará a requisição. Ambas já foram criadas acima, e o contrato foi desenvolvido utilizando essas duas classes.

Apesar de mudar a forma que desenvolvemos, não há muita complexidade, já que basta instanciarmos uma das classes (dependendo do contexto), abastecer as respectivas propriedades e enviá-la para o WCF, que seguirá os atributos definidos para configurar o formato da mensagem que está sendo enviada. Se analisarmos a classe que representará o serviço, veremos o contrato IContrato implementado nela, e com isso podemos analisar e compreender as classes que representam as mensagens:

public class Servico : IContrato
{
    public ResultadoDaBusca RecuperarSaldo(CriterioDeBusca criterio)
    {
        List<Saldo> saldos = new List<Saldo>(criterio.Contas.Length);
        ResultadoDaBusca resultado =
            new ResultadoDaBusca() { Cliente = criterio.Cliente, Codigo = criterio.Codigo };

        foreach (var item in criterio.Contas)
            saldos.Add(new Saldo() { Conta = item, Valor = 1.0M });

        resultado.Saldos = saldos.ToArray();
        return resultado;
    }
}

Em termos de hosting e binding, nada muda. Já do lado do cliente, ao fazer a referência devemos nos atentar a um pequeno detalhe, que é justamente a criação dos contratos de mensagem. Por padrão, quando fazer a referência através da IDE do Visual Studio ou através do utilitário svcutil.exe, ele não irá criar o proxy adequadamente, ou seja, ele sempre irá trabalhar no estilo RPC, o que nos obrigará a passar os parâmetros individualmente para cada operação.

Quando utilizamos a IDE e queremos que ele mantenha o estilo Messaging, então devemos ir até as configurações da referência do serviço, e marcar a opção “Always generate message contracts”, ou se estiver utilizando o svcutil.exe, utilize a opção /mc. Isso irá nos permitir a trabalhar de uma forma semelhante ao que vemos no exemplo abaixo:

using (ContratoClient proxy = new ContratoClient())
{
    CriterioDeBusca cb =
        new CriterioDeBusca("Israel Aece", 5, new string[] { "12345-6", "09876-2" });

    ResultadoDaBusca rb = proxy.RecuperarSaldo(cb);

    foreach (Saldo s in rb.Saldos)
        Console.WriteLine("{0}: {1:C2}", s.Conta, s.Valor);
}

Apesar de você mudar a forma que você desenvolve ou consome os serviços no estilo Messaging, não há muitas dificuldades. A grande mudança fica por parte do formato da mensagem que é criada pelo WCF, já que respeitará todos os atributos que decoramos nas classes que representarão as mensagens. Se analisarmos o tracing da mensagem que está sendo trafegada entre o serviço e o cliente, teremos um resultado como este (alguns trechos foram omitidos por questões de espaço):

<s:Envelope>
  <s:Header>
    <Action s:mustUnderstand="1">.../IContrato/RecuperarSaldoResponse</Action>
    <h:Cliente>Israel Aece</h:Cliente>
    <h:Codigo>5</h:Codigo>

    <ActivityId>dc2132ac-46c5-495d-a313-e9bb7152fe0a</ActivityId>
  </s:Header>
  <s:Body>
    <contas xmlns="http://tempuri.org/">
      <Saldos>
        <d4p1:Saldo>
          <d4p1:Conta>12345-6</d4p1:Conta>
          <d4p1:Valor>1.0</d4p1:Valor>
        </d4p1:Saldo>
        <d4p1:Saldo>
          <d4p1:Conta>09876-2</d4p1:Conta>
          <d4p1:Valor>1.0</d4p1:Valor>
        </d4p1:Saldo>
      </Saldos>
    </contas>
  </s:Body>
</s:Envelope>

Se analisarmos detalhadamente a mensagem gerada, veremos que os campos Cliente e Codigo fazem parte do header da mensagem, enquanto o array Saldos é parte do corpo da mensagem, que está envolvido (wrapped) pelo elemento <contas />.

Conclusão: Vimos no decorrer deste artigo uma nova forma de trabalhar com o WCF, utilizando o contrato de mensagens. É importante dizer que na maioria das vezes, você utilizará o estilo “tradicional”, onde você deverá recorrer ao estilo RPC, que já é o padrão. Em casos mais específicos, como aqueles que comentamos acima, então esse estilo de comunicação poderá dar uma maior flexibilidade, já que permitirá ao cliente ou ao serviço, ler ou gerar uma mensagem em um formato diferente daquele que o WCF cria automaticamente.

MessageContracts.zip (66.48 kb)

Tags: ,

Interoperabilidade | WCF

Migrando de ASMX para WCF

by Israel Aece 12. May 2009 20:25

Junto com a primeira versão do Visual Studio .NET e do .NET Framework, temos a possibilidade de criarmos serviços Web, baseados em XML e utilizando a tecnologia ASP.NET Web Services (ASMX). Isso ainda continua disponível nas templates de projeto da versão mais atual do Visual Studio .NET, mas, para a criação de novos projetos, ou melhor, de novos serviços, o ideal é recorrer ao WCF – Windows Communication Foundation.

De qualquer forma, os ASP.NET Web Services já existem há algum tempo e há muitas aplicações que ainda o utilizam, e este artigo ajudará a entender melhor as diferenças entre ASMX e o WCF, desde a sua estrutura de projeto até detalhes relacionados à execução do mesmo. Cada uma das seções a seguir irá analisar e discutir essas mudanças, falando também sobre alguns detalhes importantes que, se não se atentar, poderá ter um comportamento “estranho” durante a execução.

Templates e Estrutura de Projeto

Quando você opta por criar um projeto ASMX, então você deve recorrer à template ASP.NET Web Service Application. Ao criar esse tipo de projeto, você poderá adicionar dentro dele arquivos com extensão *.asmx. Esse tipo de arquivo representará um serviço. Dentro desta classe, teremos os métodos que queremos publicar. Vale lembrar que o modificador de acesso do método (public, private, etc.) não tem validade. O que determinará a visibilidade do método é se ele estiver ou não decorado com o atributo WebMethodAttribute.

Além disso, a classe que representa o serviço pode, opcionalmente, herdar da classe WebService. Essa classe fornece acesso direto aos objetos ASP.NET, como Application e Session. Assim como nas aplicações ASP.NET tradicionais (UI), o arquivo ASMX apenas possui uma diretiva chamada @WebService, que define alguns atributos utilizados pelo compilador do ASP.NET. As classes necessárias para trabalhar com o ASMX estão contidas no Assembly System.Web.Services.dll e debaixo do namespace System.Web.Service.

Já com o WCF, trabalhamos de forma bem parecida. Para trabalhar com ele, é necessário que você, ao criar um novo projeto, escolha a template WCF Service Application. Neste projeto, adicionaremos arquivos com extensão *.svc, que representará o serviço. Esse tipo de arquivo também possui uma diretiva própria, chamada de @ServiceHost, que também leva informações ao compilador do ASP.NET. As classes necessárias para trabalhar com o WCF estão contidas no Assembly System.ServiceModel.dll e debaixo do namespace System.ServiceModel.

Apesar de existirem templates distintas para cada uma das tecnologias, isso não quer dizer que você está obrigado a criar um projeto específico para os teus serviços. Caso você já possua uma aplicação ASP.NET criada, é perfeitamente possível adicionar arquivos com extensão *.asmx ou *.svc neste projeto. Uma aplicação ASP.NET (UI) consegue coexistir com qualquer uma dessas tecnologias.

Contratos

O ASMX irá se basear nos métodos decorados com o atributo WebMethodAttribute para gerar o documento WSDL. Você deverá controlar a visibilidade dos teus métodos adicionando um removendo este atributo. Qualquer tipo complexo referenciado nos métodos, será automaticamente inserido na descrição do serviço sem nenhuma configuração extra.

Já o WCF trabalha de forma bem diferente. Ele utiliza interfaces para determinar os contratos que o serviço possui. Essas interfaces são aquelas tradicionais, que já utilizamos no nosso dia-à-dia, mas decorada com um atributo chamado ServiceContractAttribute. Dentro das interfaces teremos os métodos, e controlamos se eles serão ou não expostos através do atributo OperationContractAttribute.

Em relação a tipos complexos vinculados ao contrato do serviço, a exposição deles será determinado pela versão do WCF que está utilizando. Se estiver utilizando a versão 3.0, então você precisará decorar essas classes com o atributo DataContractAttribute, e para cada propriedade que desejar expor, decorá-la com o atributo DataMemberAttribute (opt-in). Com o Service Pack 1 para o .NET Framework 3.5, esses parâmetros são opcionais (opt-out), tornando-os POCOs. Mas há um detalhe: quando você decorar a classe com o atributo DataContractAttribute, então você deverá explicitamente determinar quais propriedades deseja disponibilizar, utilizando o atributo DataMemberAttribute.

Há um outro detalhe importante durante a execução das operações. Quando você utiliza ASMX, ele concatena o namespace com o nome da mensagem para determinar o Action. O WCF concatena o namespace, o nome do serviço e o nome da operação. Para manter compatibilidade com possível clientes ASMX, você deve manter a mesma fórmula, e para isso, pode recoorer a propriedade Action do atributo OperationContextAttribute.

Serialização/Deserialização

A serialização e deserializaão estão condicionadas ao serializador que cada tecnologia utiliza. O ASMX utiliza o XmlSerializer (System.Xml.Serialization) para transformar os objetos em XML e vice-versa. O XmlSerializer serializa todos os membros públicos (propriedades e campos), sem a necessidade de definir algum atributo. Você ainda pode controlar como essa serialização será realizada, utilizando vários atributos que existem debaixo deste mesmo namespace. Para maiores detalhes sobre o funcionamento do XmlSerializer e dos atributos, consulte este artigo.

O WCF, por outro lado, utiliza o serializador DataContractSerializer por padrão. Este serializador trabalha de forma semelhante ao XmlSerializer, com poucos diferenças. Entre essas diferenças temos o entendimento por parte do DataContractSerializer do atributo SerializableAttribute, para manter a compatibilidade com objetos que foram criados para serem utilizados pelo .NET Remoting. Além disso, uma outra diferença é a capacidade que este serializador tem de persitir também membros definidos como private e protected. Este serializador ainda gera um XML mais simplificado, melhorando a interoperabilidade entre as plataformas. Se desejar utilizar o XmlSerializer, então basta decorar o seu contrato com o atributo XmlSerializerFormatAttribute. Somente utilize o XmlSerializer para cenários onde você precisa ter controle total sob como o XML é gerado.

Ainda temos um terceiro serializador que é o NetDataContractSerializer. A diferença em relação ao DataContractSerializer é que ele armazena no XML gerado, informações a respeito do tipo (CLR), como versão, assembly e full name. Este serializador é rico nas informações referente ao tipo, ele compartilha tipos, ao contrário do DataContractSerializer, que compartilha contratos. Este ainda possui baixa interoperabilidade, e pode ser utilizado em cenários onde se tem .NET Remoting.

Protocolo/Hosting

O ASMX pode somente ser hospedado no IIS, utilizando o protocolo HTTP/HTTPS. Já o WCF tem uma arquitetura muito mais flexível e é independente do protocolo, ou seja, ele pode rodar em HTTP, HTTPS, TCP, MSMQ, etc. Isso quer dizer que ao construir um serviço através do WCF, você não deve mencionar e/ou confiar em nenhum momento que o mesmo será exposto através de um determinado protocolo, já que você não conseguirá garantir isso.

O WCF também pode utilizar o IIS como host. Mas além dele, podemos fazer uso de outras aplicações para expor um serviço, como um Windows Service, ou ainda, uma simples aplicação Windows/Console. Para mais detalhes sobre cada um dos tipos de host, consulte este artigo.

Extensibilidade

O ASMX permite você interceptar as requisições através de SOAP Extensions. Com elas, podemos acoplar um código customizado no processamento da mensagem, efetuando logs, tratando a autenticação/autorização, etc. A estrutura para a criação de um extensão é simples: basta herdar da classe SoapExtension e implementar o método ProcessMessage. Como parâmetro, este método traz uma propriedade chamada Stage, que podemos identificar o estágio do processamento da mensagem. Ela nos fornece quatro opções auto-explicativas: BeforeSerialize, AfterSerialize, BeforeDeserialize e AfterDeserialize. Para utilizá-la, basta criar um atributo que herda de SoapExtensionAttribute, e sobrescrever o método ExtensionType.

O WCF traz inúmeros pontos de extensibilidade tanto do lado do serviço quanto do cliente. Através destes pontos, podemos interceptar e inspecionar os parâmetros que estão sendo enviados, a escolha da operação a ser disparada, a criação da instância da classe que representa o serviço, a serialização e deserialização da mensagem (é o que a classe SoapExtension faz), entre outros. O WCF fornece vários tipos (interfaces e classes) para você customizar o que for necessário. Para entender detalhadamente sobre todos as possibilidades que temos, consulte este artigo.

Segurança

O ASMX pode confiar somente na segurança baseada no transporte, ou seja, ele somente será seguro se você expor o serviço através de HTTPS. Você somente conseguirá abrir mão do HTTPS se utilizar a segurança baseada na mensagem, que está disponível no ASMX através do WSE – Web Services Enhancements. Muitas vezes se utiliza um SoapHeader com usuário e senha. Isso somente terá alguma segurança se utilizar HTTPS ou segurança em nível de mensagem. Do contrário, qualquer um que intercepte a requisição, conseguirá extrair o conteúdo da mensagem e seus respectivos headers.

Como já era de se esperar, o WCF fornece ambos níveis de segurança nativamente. São configurações que você realiza (de forma imperativa ou declarativa), e que o serviço utilizará para efetuar a autenticação e autorização do cliente. Uma das grandes dificuldades que o pessoal encontra ao utilizar o WCF, é que se configurar o WCF para autenticar o cliente através de usuário e senha, ainda assim será necessário utilizar um certificado para garantir a segurança.

Configuração

O ASMX possibilita que algumas configurações sejam feitas de forma declarativa, ou seja, aquela que é realizada através do arquivo Web.config. Entre essas configurações, temos a possibilidade de definir as SoapExtesions, página de ajuda/WSDL customizada, os protocolos que podem ser utilizados para acessar o serviço (HttpSoap, HttpPost e HttpGet) e mais algumas outras.

No WCF, a seção de configurações são extremamente ricas. Grande parte de tudo que utilizamos no WCF pode ser configurado a partir do arquivo de configuração. Segurança, transações, know types, behaviors, bindings, endpoints, contratos, etc. O arquivo de configuração é tão rico que as vezes chega a ser complexo. A Microsoft melhorou isso no WCF 4.0, como já foi discutido neste artigo. Aqui não há muito o que se comparar, já que grande parte do que temos no WCF não existe nativamente no ASMX. Devido a isso, muitos desenvolvedores experientes na construção de serviço utilizando o ASMX, sofrem bastante quando passam a usar o WCF.

Compatibilidade com o ASP.NET

Dentro de métodos criados no ASMX, você pode tranquilamente acessar os objetos nativos do ASP.NET, como caching, Session, Application, Cookies, etc. Você pode utilizar esses repositórios para manter as informações de acordo com o contexto. Todas essas informações são disponibilizadas através da propriedade estática Current da classe HttpContext.

A configuração padrão do WCF não permite você acessar essas informações. Na verdade, isso não é uma boa prática. Se fizer uso dessas classes dentro do seu serviço, ele ficará dependente do protocolo HTTP. Se quiser expor o mesmo serviço através de TCP ou MSMQ, essas informações não terão nenhuma utilidade. De qualquer forma, se quiser manter a compatibilidade e continuar utilizando os mesmos recursos, então você deverá explicitamente habilitá-los. Para fazer isso, você deve decorar a classe que representa o serviço com o atributo AspNetCompatibilityRequirementsAttribute, definindo a propriedade RequirementsMode como Required.

Dependendo do tempo de vida e do escopo que deseja manter alguma informação, você pode recorrer a técnicas nativas do WCF, como o compartilhamento de estado, utilizando a interface IExtension<T>, como é abordado no final deste artigo.

Interoperabilidade

Há várias especificações que foram definidas por grandes players do mercado, que regem a estrutura do envelope SOAP para suportar alguma funcionalidade. Essas especificações são conhecidas como WS-* e estão divididas em várias categorias, sendo Messaging, Security, Reliable Messaging, Transaction, Metadata, entre outros. Cada uma das empresas utiliza essas especificações e as implementam em sua plataforma. Como todos seguem (teoricamente) as mesmas especificações, haverá interoperabilidade entre serviços construídos em plataformas diferentes.

O ASMX não possui nativamente suporte a elas. A Microsoft criou um Add-on para o Visual Studio .NET, chamado WSE – Web Services Enhancements, que atualmente está na versão 3.0. Ao instalá-lo, além de várias ferramentas que ele adiciona na IDE para auxiliar na configuração destes protocolos, adiciona o runtime necessário para fazer tudo isso funcionar. É importante dizer que o WCF consegue também interoperar com o WSE, já que ambos implementam os mesmos padrões.

Como esses padrões já foram implementados nativamente no WCF, não exige nenhum complemento. Todas as especificações são configuradas através do binding, podendo inclusive efetuar essa configuração através do arquivo Web.config. Antes de habilitar ou desabilitar essas funcionalidades, é importante que se tenha o devido conhecimento, para evitar qualquer problema ao até mesmo criar alguma vulnerabilidade. 

Ainda falando sobre interoperabilidade, a Microsoft se preocupou em manter os investimentos feitos com o ASMX. O WCF consegue facilmente conversar com serviços construídos em ASMX em ambos os lados, ou seja, você pode construir um serviço ASMX e consumí-lo com a infraestrutura do WCF, bem como pode construir um serviço escrito em WCF e consumí-lo com a infraestrutura do ASMX. Na verdade, quando você for criar um novo serviço, opte sempre pelo WCF. Você somente teria essa interoperabilidade entre essas tecnologias, quando desejar substituir o ASMX pelo WCF em serviços que já estão rodando, e com vários clientes dependendo do mesmo.

Internals

As requisições para arquivos *.asmx são direcionadas pelo IIS para o ISAPI do ASP.NET (aspnet_isapi.dll). Em um determinado momento, o ISAPI direciona a requisição do pedido para o código gerenciado. As requisições para estes arquivos tem como alvo um dos handlers WebServiceHandlerFactory ou ScriptHandlerFactory. A primeira delas, se baseando no arquivo requisitado, construirá dinamicamente a classe que representa o serviço. Já a classe ScriptHandlerFactory construirá o handler baseando-se em uma requisição REST/AJAX.

O WCF também utiliza o pipeline do ASP.NET, e quando a requisição é entregue pelo IIS para o ASP.NET, ele verifica se trata-se de uma requisição para um arquivo *.svc. Caso positivo, o handler responsável pelo processamento do pedido é o HttpHandler (System.ServiceModel.Activation). Internamente o WCF faz o processamento síncrono da operação, não liberando a thread do ASP.NET, que ficará bloqueada até a finalização da operação. Para entender melhor esse problema e também como melhorá-lo, você pode recorrer a este artigo.

Deployment

Assim como qualquer aplicativo .NET, basta mover os serviços para o IIS remoto e tudo já funciona. Obviamente que você deverá se certificar que você tenha a mesma versão do .NET Framework (isso inclui o Service Packs) instalada no servidor. É importante dizer que ambas tecnologias necessitam de um diretório virtual devidamente criado no IIS, com as permissões também configuradas. Apenas atente-se ao WCF, que tem um pequeno bug quando você opta pela pré-compilação do projeto.

Cliente

Dentro do Visual Studio .NET você tem duas opções para referenciar um serviço: “Add Web Reference” e “Add Service Reference”. Podemos dizer que com a primeira opção, você deve utilizar quando for referenciar um serviço ASMX em uma aplicação; já com a segunda opção, você deve utilizar quando você for referenciar um serviço WCF em uma aplicação. Quando fizer isso, a IDE criará o proxy utilizando a API da tecnologia correspondente.

Isso não quer dizer que você precisa seguir sempre esta regra. Você pode referenciar um serviço ASMX na opção “Add Service Reference”. Ao fazer isso, toda a estrutura do lado do cliente, será criada utilizando a API do WCF, ou seja, o proxy será baseado na classe ClientBase<TChannel> ao invés da SoapHttpClientProtocol, e toda a configuração do endpoint do serviço será colocada no arquivo Web.config. O WCF criou um binding chamado BasicHttpBinding para a interoperabilidade com o ASMX, que você pode utilizar para interagir com o serviço criado através do ASMX, sem maiores problemas.

Conclusão: Como vimos neste artigo, há vários detalhes que precisamos nos atentar na construção ou migração de um serviço escrito em ASMX para WCF. Para efeito de compatibilidade, algumas funcionalidades continuam trabalhando da mesma forma, enquanto para outras há um jeito diferente de fazer, e que na maioria das vezes, acaba sendo mais flexível. O artigo também não aborda detalhadamente cada uma das funcionalidades do WCF. Se desejar construir um serviço utilizando WCF, ou se quiser entender mais detalhadamente cada uma das funcionalidades abordadas aqui, pode consultar este artigo, que no final dele, há uma relação com os principais artigos que falam sobre essas funcionalidades.

Tags: , , , ,

WCF | CSD | Interoperabilidade | Security

Embutindo o PIA

by Israel Aece 24. April 2009 22:57

Quando referenciamos um componente COM em um aplicativo .NET, o Visual Studio .NET nos auxilia na criação de um Assembly de Interoperabilidade, conhecido como Primary Interop Assembly, ou somente PIA. Na verdade, a IDE recorre à um utilitário chamado tlbimp.exe, que dado um determinado componente COM, gera uma DLL com toda estrutura (tipos) do componente COM, já “traduzindo” os tipos COM para tipos .NET, facilitando assim o consumo por uma aplicação .NET.

Uma das melhorias que o .NET 4.0 está trazendo é a possibilidade de embutirmos esse Assembly de interoperabilidade no Assembly da aplicação. Para isso, basta ir até as propriedades da referência COM e definir a propriedade Embed Interop Types como True. Isso evita ter um Assembly exclusivo para servir como “ponte” entre a aplicação .NET e o componente COM. Lembro-me de alguns poucos projetos que tivemos que utilizar esse recurso que, como se já não bastasse o overhead que é causado pelo RCW, a distribuição era terrível, já que tínhamos que registrar efetivamente o componente COM no Windows, e instalar o Assembly de interoperabilidade e a aplicação em si.

Além de facilitar a distribuição, esse recurso ainda se preocupa em inserir no Assembly da aplicação somente os tipos necessários que a mesma utiliza, diminuindo consideravelmente o tamanho final da mesma. Podemos notar isso através da imagem abaixo:


PIA

Tags: ,

.NET Framework | Interoperabilidade

WWS - Windows Web Services

by Israel Aece 11. January 2009 08:18

Sendo publicado como um componente a partir do Windows 7, o Windows Web Services - WWS, é uma API que permite ao código nativo, não gerenciado, criar e consuimir Web Services sem a necessidade do .NET Framework. O propósito de ser um componente é que a Microsoft pretende disponibilizá-lo para as outras versões do Windows, como XP, Vista, 2003 e 2008.

Com um modelo bem similar ao WCF, com bindings, channels, endpoints, etc., o WWS facilita a criação e hosting de serviços, bem como o consumo deles, fornecendo interoperabilidade com o WCF, ASMX e outras tecnologias não-Microsoft, que seguem os padrões WS-*.

Os tres grandes pilares do WWS são: Service Model, Channel LayerXML Layer. O primeiro deles gerencia a comunicação, ou seja, se estivermos consumindo o serviço, ele fornece tipos para facilitar o seu consumo (service proxy); já se estivermos expondo o serviço, então ele fornece tipos para efetuar o hosting (service host). A Channel Layer controla a comunicação, como os dados que são enviados e recebidos, abstração do protocolo, segurança, etc. E, finalmente, temos a XML Layer, fornece acesso completo ao conteúdo das mensagens.

Algumas funcionalidades ainda não foram implementadas, como é o caso do suporte a segurança em nível de mensagem e, pelo que parece, isso irá acontecer de acordo com a necessidade dos clientes. Assim como o WCF, o WWS também é orientado a contratos, permitindo trabalhar de forma bem semelhante no código não gerenciado. O WWS também fornece um utilitário chamado WsUtil.exe, que mapeia o WSDL/XSD para tipos C.

Para aqueles que possuem o Windows 7, verá que no diretório %windir%\System32\ existe uma DLL chamada Webservices.dll que gerencia grande parte desta comunicação. Além disso, o Windows SDK disponibilizado no PDC 2008, traz esta API (WWSAPI), contendo o Wsutil.exe, Webservices.h e webservice.lib.

É importante dizer que, segundo Nikola Dudar, o WWS não substitui o WCF. A finalidade do WWS é possibilitar aplicações nativas consumirem ou disponibilizarem um serviço, enquanto o WCF é a alternativa para o código gerenciado, e depende do .NET Framework.

Tags:

CSD | Interoperabilidade

Algumas novidades interessantes

by Israel Aece 28. October 2008 09:04

A versão CTP do Visual Studio 2010 com o .NET Framework 4.0 já está disponível, assim como o Soma Somasegar disse neste post. Olhando superficialmente, há algumas funcionalidades que achei bastante interessantes, que quero listar aqui:

  • Code-snippet para HTML.
  • ClientIDMode: Nova propriedade que está contida nos controles ASP.NET, dando-nos a possibilidade de definir como renderizar o ID dos controles.
  • Gráficos: Agora temos nativamente um controle chamado Chart, mas que pode também ser utilizado em conjunto com o Visual Studio 2008 e .NET Framework 3.5. Download: Controles, VS.NET Toolbox (Add-On) e Documentação.
  • Web.config: Possibilidade de criar/transformar o seu arquivo Web.config para diversos estágios do desenvolvimento (Debug/Staging/Release) que, na maioria das vezes, as configurações mudam em cada uma dessas fases.
  • PIA - Primary Interop Assemblies: Quando adicionamos uma referencia a algum componento COM, era criado um Assembly de interoperabilidade em nosso projeto. Esse Assembly apenas contém a estrutura do componente não gerenciado e, que devemos distribuir juntamente com a aplicação onde ele foi referenciado. Com o Visual Studio 2010, podemos embutir esses tipos no Assembly da aplicação/DLL onde o componente está sendo referenciado, sem a necessidade da criação de um Assembly a parte.
  • Computação Paralela: Até então chamada de Parallel Extensions, agora ela foi incorporada dentro do .NET Framework.
  • VB.NET - Propriedades Automáticas: Assim como no C#, o VB.NET agora suporta propriedades automáticas. A sintaxe para isso é: Public Property Nome As String.
  • C# - Parametros Opcionais: Assim como nas versões do Visual Basic, o C# agora passa a suportar parametros opcionais e, assim como no VB, é necessário especificar um valor padrão. A sintaxe para isso é: public void Teste(string nome, int valor = -1){ }.
  • C# - Tipos Dinamicos: Assim como Charlie Calvert comentou aqui há algum tempo, o C# agora terá uma keyword chamada dynamic. Ao declarar uma variável como dinamica, ela suportará a chamada de membros e, a verificação se eles existem ou não, somente acontecerá em tempo de execução. Isso também é conhecido como late-binding.

Tags: ,

.NET Framework | ASP.NET | C# | Interoperabilidade | VB.NET

WCF e a interoperabilidade COM

by Israel Aece 29. September 2008 09:12

O WCF fornece diversos meios de interoperabilidade com tecnologias existentes e, entre elas, a possibilidade de consumir um serviço a partir de aplicações COM, como é o caso do Visual Basic 6.0, C ou mesmo através do ASP Clássico. Isso permitirá que aplicações desenvolvidas nessas tecnologias façam o uso de serviços construídos em WCF sem muito trabalho.

A criação do serviço e a forma como voce irá expor não mudará. Atualmente temos duas alternativas para possibilitar essa interoperabilidade: a primeira delas seria fazer o uso do utilitário svcutil.exe, criando o arquivo cs/vb representando o proxy e também o seu respectivo arquivo de configuração. Com isso, podemos criar um Assembly (atente-se ao atributo ComVisibleAttribute) gerenciado e, a partir daqui, voce poderá expor o tlb e consumí-lo em aplicações COM (para maiores detalhes sobre este processo, consulte este link). Utilizando esta técnica, será necessário registar o Assembly e colocar o arquivo de configuração gerado pelo svcutil.exe no mesmo diretório do *.exe, com o mesmo nome da aplicação acrescido de .config, como por exemplo: AplicacaoEmVB6.exe.config. A segunda possibilidade e mais simples, é acessar diretamente o serviço que está rodando, sem a necessidade de expor um Assembly gerenciado ou até mesmo registrar o componente dentro do Windows.

Para ambos os casos, a aplicação cliente fará o uso do service moniker. Este elemento está disponibilizado a partir da função GetObject que, por sua vez, retorna uma instancia do objeto especificado como parametro (ProgID) e que para serviços WCF, essa instancia representará o canal de comunicação, ou seja, o proxy. No caso de clientes COM que farão o uso de um serviço WCF, é comum especificar como parametro o endereço do serviço (HTTP, TCP, etc.), endereço do WSDL, o contrato e o binding a ser utilizado pela aplicação.

O código abaixo ilustra como consumir o serviço WCF a partir do ASP Clássico, utilizando a segunda alternativa de interoperabilidade comentado acima:

<%

    Dim proxy
   
    Set proxy = GetObject("service:mexAddress=net.tcp://localhost:9292/mex, " & _
        "address=net.tcp://localhost:9292/srv, " & _
        "contract=IOperacao, contractNamespace=http://www.projetando.net, " & _
        "binding=NetTcpBinding_IOperacao, bindingNamespace=http://tempuri.org/")
   
    Response.Write(proxy.Adicionar(3, 34))

%>

Neste caso, o moniker utilizará os dados disponibilizados pelo WSDL para recuperar as informações necessárias e, possibilitar a chamada para o serviço, diferentemente do primeiro caso, onde disponibilizamos o proxy criado para o mundo COM através dos próprios recursos nativos do .NET Framework. Caso utilizarmos a primeira possibilidade de interoperabilidade, as únicas informações que devemos passar para a função GetObject são o endereço e o binding.

Tags: , ,

CSD | Interoperabilidade | WCF

ComVisibleAttribute

by Israel Aece 7. July 2008 12:29

Este atributo está presente desde a versão 1.0 do .NET Framework. Ele tem a finalidade de controlar a acessibilidade de um membro ou tipo para o mundo COM. Já falei sobre ele aqui.

Esse atributo recebe em seu construtor um valor booleano indicando tal acessibilidade, com o valor padrão definido como True. Sendo assim, quando este atributo é omitido, assume-se que ele será exposto ao mundo COM. Só que há uma mudança no tipo de projeto Class Library entre a versão 1.x para a versão 2.0, mais precisamente no Visual Studio 2005. Quando criamos um projeto do tipo Class Library agora, esse atributo vem explicitamente definido como False, no arquivo AssemblyInfo.cs, impedindo que qualquer membro dentro deste Assembly seja visível.

Com isso, sempre que precisar criar um Assembly para hospedá-lo no COM+ ou até mesmo para interoperar com o mundo não gerenciado e, estiver criando sob o Visual Studio 2005, atente-se e defina este atributo como True.

Tags: ,

.NET Framework | Interoperabilidade

WCF - Expondo componente COM+

by Israel Aece 5. July 2008 10:22

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);
    }
}

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
        }
    }
}

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}" %>

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>

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.

CompenenteCOMPlus.zip (55.84 kb)

Tags: , ,

CSD | Interoperabilidade | WCF

Powered by BlogEngine.NET 1.5.0.0
Theme by Mads Kristensen

Sobre

Meu nome é Israel Aece e sou especialista em tecnologias de desenvolvimento Microsoft, atuando como desenvolvedor de aplicações para o mercado financeiro utilizando a plataforma .NET. [ Mais ]

Twitter

Host