Twitter

Recentemente eu criei uma conta no Twitter. Na verdade eu não uso aquilo como deveria ser, apenas utilizo para divulgar novos artigos e posts que publico. Se você já assina este RSS, em princípio, me seguir no Twitter é redundante.

Reflector – Assembly List Management

Grande parte dos desenvolvedores .NET dificilmente não conhecem o .NET Reflector. Essa ferramenta permite decompilar Assemblies escritos em C#/VB.NET, permitindo o entendimento de como uma funcionalidade trabalha internamente. Quanto mais viciado você vai ficando nesta ferramenta, mais Assemblies são carregados dentro dela e, rapidamente, a lista cresce bastante, ficando difícil a navegação entre eles. Uma das funcionalidades que esta ferramenta disponibiliza é chamada de Assembly List Management. Com ela, podemos criar categorias e carregarmos dentro delas somente Assemblies específicos, ficando a sua escolha.

A imagem abaixo exibe a lista que tenho atualmente:

WCF – MessageContracts

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/”&gt;
      <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)

Migrando de ASMX para WCF

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.

Procurando por MCTs

Como você já deve ter visto aqui, eu ministro treinamentos Microsoft na área de desenvolvimento, em um centro oficial em Campinas – SP, chamado People Computação.

Como está havendo uma grande demanda para cursos de .NET 2.0 e 3.5, ela está recrutando MCTs que possuem alguma certificação MCPD (exigência da Microsoft) e que esteja disposto e tenha disponibilidade para ministrar tais treinamentos. Se você estiver interessado, entre em contato comigo para que eu possa indicá-lo e colocá-lo em contato com a pessoa responsável. Questões de cronograma, valores, etc., deverão ser tratados diretamente com a People.

Embutindo o PIA

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

WWS – Windows Web Services

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.

Algumas novidades interessantes

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.

WCF e a interoperabilidade COM

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.