Conteúdo de WCF para estudo

Muitas pessoas me escrevem para indicar algum material sobre WCF para guiar o estudo de alguém que esteja disposto a aprender esta tecnologia. Como eu já tenho bastante conteúdo escrito sobre isso, resolvi criar um post para elencar todos eles, em uma ordem de leitura que você poderia seguir até atingir este objetivo. Só é importante ressaltar que estes artigos foram escritos individualmente, e podem não ser tão intuitivos quanto um livro, mas acredito que ajudará a dar os primeiros passos.

Observação: Nem todos os posts que já escrevi sobre o WCF estão mencionados aqui. Pode haver informações, não menos importantes, que você pode encontrar na seção de busca ou clicando na categoria WCF.

WCF – Usando o MsmqIntegrationBinding

Há algum tempo eu mostrei neste artigo como podemos fazer o uso do Message Queue com WCF, e lá foi discutido todos os benefícios de se envolver uma fila na comunicação entre as duas partes. A exigência daquele modelo de utilização é que ambos os lados precisam utilizar a API do WCF para se comunicar com o Message Queue.

O problema é que as vezes já temos uma solução implementada e operante. Antes do WCF, a única forma que tínhamos para interagir com o Message Queue na plataforma .NET era através das classes contidas no assembly System.Messaging.dll. Além disso há outras soluções que recebem ou enviam mensagens para uma fila, escritas em outras tecnologias.

Para trabalhar com Message Queue no WCF, temos dois bindings: NetMsmqBinding e MsmqIntegrationBinding, onde o primeiro deles já foi esgotado naquele outro artigo. O segundo trata-se de um binding criado exclusivamente para interagir com filas em que as mensagens sejam postadas ou recebidas por outras tecnologias que não o WCF. A finalidade deste artigo é mostrar como proceder para utilizar este binding e interoperar com soluções que já estão em funcionamento.

Para exemplificar vamos interoperar com aplicativos que recebem e postam mensagens utilizando a API System.Messaging.dll, que existe desde a primeira versão do .NET Framework. Abaixo temos dois cenários distintos, separados em duas pastas: WCFNoCliente e WCFNoServidor. No primeiro cenário utilizaremos o WCF para enviar uma mensagem para a fila e o serviço irá extrair a mensagem de lá utilizando as classes contidas no assembly System.Messaging.dll. No segundo cenário, será o inverso, ou seja, utilizaremos as classes contidas no assembly System.Messaging.dll para enviar a mensagem, e utilizaremos o WCF no serviço para extrair a mensagem da fila e processá-la. A imagem abaixo ilustra a estrutura dos projetos de teste:

O primeiro passo é desenhar o contrato do serviço, que irá formalizar a comunicação. A ideia será ter um serviço que processa pedidos de algum comércio eletrônico. Sendo assim teremos duas classes: Pedido e Cliente, que são autoexplicativas. Cada uma delas tem propriedades que as descrevem, que também não precisam ser exibidas aqui. Uma característica de serviços que são expostos através do Message Queue, é que as operações eles devem ser sempre one-way, assim como já foi discutido anteriormente. Abaixo temos a definição do contrato que será utilizado como exemplo:

[ServiceContract]
[ServiceKnownType(typeof(Pedido))]
[ServiceKnownType(typeof(Cliente))]
public interface IComercioEletronico
{
    [OperationContract(IsOneWay = true, Action = “*”)]
    void AdicionarPedido(MsmqMessage<Pedido> mensagem);
}

Infelizmente por se tratar de um cenário específico, a nossa operação deverá ter apenas um único parâmetro, e ele deve ser do tipo MsmqMessage<T>, que está debaixo do namespace System.ServiceModel.MsmqIntegration, e representará o body da mensagem. Caso você precise de mais parâmetros, então o correto é que você inclua isso em uma mesma classe e a defina como o parâmetro genérico T da classe MsmqMessage<T>. É importante dizer que o contrato irá definir a estrutura da mensagem a ser enviada para a fila (WCFNoCliente) ou a mensagem a ser extraída da fila (WCFNoServidor).

um detalhe importante na criação do contrato é o uso do atributo ServiceKnownTypeAttribute, que é exigido para todos os elementos mencionados direta ou indiretamente no contrato. Isso é necessário para que o serializador conheça todos os tipos envolvidos, caso contrário, a mensagem não será recebida pelo WCF, e ao ligar o tracing, vamos nos deparar com a seguinte mensagem de erro:

System.ServiceModel.ProtocolException: An error was encountered while deserializing the message. The message cannot be received. —&gt; System.Runtime.Serialization.SerializationException: An error occurred while deserializing an MSMQ message’s XML body. The message cannot be received. Ensure that the service contract is decorated with appropriate [ServiceKnownType] attributes or the TargetSerializationTypes property is set on the MsmqIntegrationBindingElement.

Utilizando o WCF no Servidor

Como será um serviço WCF que irá extrair as mensagens da fila, o primeiro passo aqui é a implementação do contrato na classe que representará o serviço. Aqui tudo é muito parecido com um serviço WCF tradicional, mas com uma pequena diferença que está na operação. Como a classe MsmqMessage<T> representa a mensagem que está dentro da fila, ela é muito mais do que a instância da classe Pedido, ou seja, temos outras propriedades que estão relacionadas ao protocolo.

Entre as várias propriedades expostas pela classe MsmqMessage<T> temos a propriedade Body, que retorna um object que representa a instância da classe que foi serializada no corpo da mensagem pelo cliente, que no nosso caso é a classe Pedido. Abaixo temos a implementação do serviço com o processamento da mensagem:

public class ServicoDeComercioEletronico : IComercioEletronico
{
    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
    public void AdicionarPedido(MsmqMessage<Pedido> mensagem)
    {
        Pedido pedido = mensagem.Body as Pedido;

        if (pedido != null)
        {
            Console.WriteLine(“—- Processando o Pedido: {0}”, pedido.Codigo);
            Console.WriteLine(“—- Cliente: {0}”, pedido.Cliente);
            Console.WriteLine(“—- Valor: {0:N2}”, pedido.ValorTotal);
            Console.WriteLine(“—- Data: {0:dd/MM/yyyy HH:mm}”, pedido.Data);
            Console.WriteLine();
        }
    }
}

Note que decoramos a operação com o atributo OperationBehaviorAttribute definindo as propriedades TransactionScopeRequired e TransactionAutoComplete para True, pois utilizando o binding MsmqIntegrationBinding não temos o controle explícito das transações, que para isso exige que o binding suporte sessões, e não é o caso do MsmqIntegrationBinding.

Depois da implementação definida, agora chega o momento de configurar o serviço. Como já sabemos, vamos recorrer ao binding MsmqIntegrationBinding. Para uma fácil interoperabilidade, o endereço em que o serviço é exposto deve utilizar o scheme msmq.formatname ao invés do net.msmq, que permite entre vários modelos, o acesso direto a fila (através de format-name), sem a necessidade de contatar o Active Directory (AD). Abaixo temos a configuração declarativa do serviço:

<system.serviceModel>
  <services>
    <service name=”Servico.ServicoDeComercioEletronico”>
      <endpoint address=”msmq.formatname:DIRECT=OS:.private$MinhaFila”
                binding=”msmqIntegrationBinding”
                bindingConfiguration=”bindingConfig”
                contract=”Biblioteca.IComercioEletronico” />
    </service>
  </services>
  <bindings>
    <msmqIntegrationBinding>
      <binding name=”bindingConfig”>
        <security mode=”None” />
      </binding>
    </msmqIntegrationBinding>
  </bindings>
</system.serviceModel>

Com o serviço criado, podemos agora inicializarmos a criação do cliente. Obviamente que não iremos referenciar o serviço, mesmo porque nem criamos o endpoint que expõe o documento WSDL. A ideia aqui será utilizar os tipos que estão dentro do assembly System.Messaging.dll para enviar a instância da classe Pedido para a fila, e utilizar o WCF do outro lado para processar.

Depois do assembly referenciado na aplicação, instanciamos a classe MessageQueue e informamos o caminho para a fila (via path-name). Depois instanciamos a classe Pedido e a configuramos com os dados do Pedido que foi realizado e, finalmente, enviamos a instância para a fila através do método Send. Neste momento, a instância da classe Pedido será serializada como o body da mensagem.

using (MessageQueue queue = new MessageQueue(@”.private$MinhaFila”))
{
    Pedido pedido = new Pedido();
    pedido.Codigo = 123;
    pedido.Data = DateTime.Now;
    pedido.ValorTotal = 1983.81M;
    pedido.Cliente = new Cliente() { Codigo = 39, Nome = “Israel Aece” };

    queue.Send(pedido, MessageQueueTransactionType.Single);
    Console.WriteLine(“Mensagem Enviada com Sucesso.”);
}

Observação: Como a fila é transacionada, precisamos definir o tipo da transação a ser utilizada pelo Message Queue no momento do envio da mensagem. Se o controle da transação está sendo controlada por um elemento externo, como é o caso do TransactionScope, então é necessário definir o MessageQueueTransactionType para Automatic, para que o envio da mensagem possa participar da mesma transação, criada previamente.

Utilizando o WCF no Cliente

Neste cenário inverteremos as tecnologias que utilizamos. Agora no serviço utilizaremos os tipos do assembly System.Messaging.dll e no cliente o WCF. Aqui criaremos a instância da classe MessageQueue com a finalidade de extrair a mensagem que lá foi postada, enviada por um cliente que utiliza o WCF. E para isso utilizaremos o método Receive, que da mesma forma que o método Send, também precisa estar protegido por uma transação.

O método Receive retorna uma instância da classe Message (namespace System.Messaging), que entre várias propriedades, temos a Body, que retorna um object representando a classe que foi serializada, e que no nosso caso é a classe Pedido. O código abaixo ilustra como fazemos para extrair a mensagem:

using (MessageQueue queue = new MessageQueue(@”.private$MinhaFila”))
{
    queue.Formatter = 
        new XmlMessageFormatter(new Type[] { typeof(Pedido), typeof(Cliente) });

    Pedido pedido = queue.Receive(MessageQueueTransactionType.Single).Body as Pedido;

    if (pedido != null)
    {
        Console.WriteLine(“—- Processando o Pedido: {0}”, pedido.Codigo);
        Console.WriteLine(“—- Cliente: {0}”, pedido.Cliente);
        Console.WriteLine(“—- Valor: {0:N2}”, pedido.ValorTotal);
        Console.WriteLine(“—- Data: {0:dd/MM/yyyy HH:mm}”, pedido.Data);
        Console.WriteLine();
    }
}

Da mesma forma que fizemos acima com o contrato do WCF, decorando-o com o atributo ServiceKnownTypeAttribute, temos também que mencionar quais sãos os tipos que compõem o body da mensagem que está na fila, e para isso podemos recorrer a instância da classe XmlMessageFormatter, e em seu contrutor informar um array contendo os tipos envolvidos.

Como não há referência do serviço no cliente, precisamos fazer com que o cliente conheça qual é o contrato do serviço, e justamente por isso que ele foi colocado em uma DLL a parte, chamada de Biblioteca. Utilizamos no cliente a classe ChannelFactory<TChannel> para criar a canal de comunicação entre a aplicação cliente e a fila.

Além do contrato, devemos também informar o binding que irá reger a comunicação, e que como sabemos, será o binding MsmqIntegrationBinding. Em seguida informarmos o endereço (format-name) da fila em que a mensagem será postada. Com a factory criada, agora podemos criar a instância do canal que é efetivamente o proxy:

using (ChannelFactory<IComercioEletronico> factory =
    new ChannelFactory<IComercioEletronico>(
        new MsmqIntegrationBinding(MsmqIntegrationSecurityMode.None),
        @”msmq.formatname:DIRECT=OS:.private$MinhaFila”))
{
    var proxy = factory.CreateChannel();

    Pedido pedido = new Pedido();
    pedido.Codigo = 123;
    pedido.Data = DateTime.Now;
    pedido.ValorTotal = 1983.81M;
    pedido.Cliente = new Cliente() { Codigo = 39, Nome = “Israel Aece” };

    proxy.AdicionarPedido(new MsmqMessage<Pedido>(pedido));
    Console.WriteLine(“Mensagem Enviada com Sucesso.”);
}

Note que depois da instância da classe Pedido criada e configurada, ela não é passada diretamente para o método AdicionarPedido. Como foi comentado acima, o contrato impõe que a classe Pedido seja envolvida por um wrapper (MsmqMessage<T>), que representará a mensagem dentro do fila, e a instância da classe Pedido será armazenada/serializada no body dela.

Assincronismo

Como sabemos, uma das características do uso do Message Queue é garantir o assincronismo, e permitir ao cliente enviar mensagens ao serviço mesmo que ele esteja offline, mascarando assim eventuais problemas que existam na comunicação entre as duas partes envolvidas.

Conclusão: No decorrer deste artigo vimos que podemos substituir a tecnologia que existe de um dos lados para uma tecnologia mais recente, como é o caso do WCF, e com isso tirar todo o proveito de seus recursos, mas tendo em mente que existem algumas poucas limitações e imposições.

UtilizandoMsmqIntegrationBinding.zip (20.48 kb)

Message Queue no WAS

Há algum tempo eu mostrei publiquei aqui um artigo que demonstra como utilizar serviços WCF em conjunto com o Message Queue, que como sabemos, leva a confiabilidade dos serviços para um outro nível. Para efeito de testes, aquele exemplo usa uma aplicação Console para hospedar o serviço que faz uso do Message Queue.

Uma das opções de hosting que temos é o WAS (Windows Activation Service), que incorpora ao IIS funcionalidades extras, permitindo ao mesmo hospedar serviços que vão além do protocolo HTTP. Quando habilitamos o WAS, ele também fornece um listener para gerenciar serviços que são hospedados no IIS mas fazem uso do Message Queue. Esse serviço chamado de Net.Msmq Listener Adapter, é o responsável pela ativação do worker process que gerencia a execução dos serviços WCF hospedados no IIS.

O serviço de ativação nada mais é que um serviço Windows, que quando é inicializado, percorre todas as filas públicas e privadas que existem no computador, qual monitorará as mensagens que chegarem para elas, e quando uma mensagem chegar, irá entregá-la para o serviço WCF, que por sua vez, irá processá-la. Mas para que ele consiga lidar tranquilamente com o Message Queue, alguns cuidados precisam ser tomados.

O primeiro é com relação ao nome da fila. Para que o WAS consiga entregar as mensagens de forma transparente, é importante que o nome da fila tenha o mesmo nome do diretório virtual, incluindo o arquivo *.svc. Por exemplo, se temos um diretório virtual chamado Servicos e o serviço WCF nomeado como ServicoDePedidos.svc, então o nome da fila deverá ser Servicos/ServicoDePedidos.svc. Outro detalhe importante é que as filas do Message Queue também são protegidas por ACLs, o que nos obriga a fazer com que a conta definida no worker process (muitas vezes o Network Service) também tenha acesso à fila em questão.

Com essas configurações, já temos os serviços fazendo uso do Message Queue e, consequentemente, explorando todo o potencial das funcionalidades que o IIS e o AppFabric nos disponibiliza.

Herança de Interfaces em contratos de serviço

Assim como as classes, as interfaces também podem fazer uso da herança para agregar funcionalidades e características de outras interfaces, e quando implementamos uma interface que herda de outra, todos os membros envolvidos na hierarquia deverão ser definidas na classe.

Como sabemos, são as interfaces que definem os contratos de serviços WCF, e podemos fazer uso de herança de interfaces para refinar o acesso à operações, conseguindo filtrar o que será exposto através de um determinado endpoint. Considere as interfaces a seguir:

[ServiceContract]
interface IContrato1
{
    [OperationContract]
    string Metodo1(string value);
}

[ServiceContract]
interface IContrato2 : IContrato1
{
    [OperationContract]
    string Metodo2(string value);
}

Note que a interface IContrato2 herda da interface IContrato1, o que obrigará a classe (serviço) que implementar a interface IContrato2, também definir a implementação para o Metodo1, definido na interface IContrato1. Como na criação do endpoint somos obrigados a informar um contrato, podemos permitir que internamente o consumidor veja ambos os métodos, pois estamos expondo o contrato IContrato2, enquanto externamente, o consumidor somente irá visualizar as operações expostas pelo contrato IContrato1, que neste caso é o método Metodo1.

host.AddServiceEndpoint(typeof(IContrato1), new BasicHttpBinding(), “contratoExterno”);
host.AddServiceEndpoint(typeof(IContrato2), new NetTcpBinding(), “contratoInterno”);

O fato de existir a herança, permitirá ao consumidor interno visualizar as operações do contrato IContrato2 e também as operações expostas pelo contrato IContrato1, dando a ele todas as funcionalidades que foram fornecidas pela herança dos contratos (interfaces).

WIF – Tracing

Há algum tempo eu comentei aqui em como habilitar e manipular o tracing em serviços WCF. Utilizando essa mesma estrutura, a Microsoft também incorporou este recurso no WIF, para possibilitar o diagnóstico de eventuais problemas ou comportamentos estranhos que possam acontecer durante o desenvolvimento ou mesmo durante o ambiente de produção.

Assim como serviços WCF, o WIF também é baseado em mensagens sendo trafegadas entre as partes envolvidas, que determinam as requisições de autenticação, a resposta informando se o usuário é válido ou não, processo de logout, etc. O tracing do WIF também é bastante completo, e nos permite visualizar as mensagens de forma gráfica, utilizando o utilitário Service Trace Viewer.

O uso deste tracing está disponível tanto para o autenticador (STS) quanto pela aplicação cliente (RP), e isso se dá através de trace sources, que foram criados pela Microsoft durante a construção do WIF. Esse trace source é chamado de Microsoft.IdentityModel, e podemos utilizá-lo em ambos os lados (STS e RP), para capturar os procedimentos que estão acontecendo nas duas partes. Abaixo temos o código para habilitar o tracing do lado do autenticador, e se quiser utilizar pelo cliente (RP), tudo o que precisa ser feito é colocar esta mesma configuração, alterando somente o nome do arquivo para que um não sobrescreva o do outro.

<system.diagnostics>
  <sources>
    <source name=”Microsoft.IdentityModel” switchValue=”All”>
      <listeners>
        <add name=”xml”
             type=”System.Diagnostics.XmlWriterTraceListener”
             initializeData=”C:TempAutenticador.xml” />
      </listeners>
    </source>
  </sources>
  <trace autoflush=”true” />
</system.diagnostics>

Dentro do arquivo gerado, os logs estarão distribuídos entre várias categorias, onde cada uma delas representa um estágio ou processo diferente. Essas categorias estão listadas abaixo, com uma descrição superficial sobre cada uma delas:

  • ChunkedCookieHandler: Nome, tamanho, domínio e data de expieração do cookie gerado para o usuário.
  • ClaimsPrincipal: Informações sobre a IPrincipal que foi criada, contendo o nome e o conjunto de claims.
  • DeflateCookie: Basicamente traz informações sobre o processo de compressão do cookie, incluindo o tamanho original e o comprimido.
  • HashTrace: Informações sobre tamanho e valor das assinaturas digitais do conteúdo Xml.
  • PassiveMessage: Contém um conjunto de informações permitinentes ao ambiente passivo, que são representadas pela requisição HTTP (wa, wresult e wctx).
  • Reference: Cataloga informações sobre o processo de cálculo dos digests (hash).
  • Token: Como o próprio nome diz, temos aqui informações sobre o token, trazendo informações a cada token específico (SessionTokens, Saml11 e Saml2).
  • WsFedMessage: Armazena informações que chegam até a aplicação consumidora, que cataloga cada estágio do processamento do token.
  • Exceptions: Problemas que ocorrem durante o processo de autenticação, incluindo toda a stack até o erro.
  • AppDomain Unloading: Informações sobre o descarregamento do AppDomain da aplicação.

System.Json no .NET Framework

Para aqueles que estiveram presentes na minha palestra sobre REST no TechEd 2010, puderam ver as opções que temos para a construção e consumo de serviços baseados neste modelo. Tive a oportunidade de mostrar de forma superficial, cada uma das tecnologias que circundam o WCF, e que foram construídas em cima dele, para atender a necessidade atual.

Já falei aqui sobre o WCF REST Starter Kit (HttpClient), que torna o consumo de serviços REST (construídos ou não em .NET) muito mais simples em aplicações .NET (Console, Windows, WPF, ASP.NET, etc.), abstraindo a necessidade de conhecer detalhes internos do protocolo HTTP, lidando facilmente com os formatos existentes, tais como: Xml, Atom e JSON.

O WCF já fornece suporte a construção de serviços REST há algum tempo. Temos a possibilidade de expor serviços neste modelo, possibilitando a serialização em qualquer um dos formatos acima, mas ainda, com um pequeno suporte para lidar com objetos que são criados dinamicamente por seus clientes. Isso quer dizer que o WCF, até então, exige que o cliente deva conhecer a estrutura do objeto que o serviço espera. O problema é que isso pode ser dinamicamente criado pelos clientes, algo que o JavaScript pode fazer muito bem.

Para ter uma integração maior com estes tipos de clientes, a Microsoft está incorporando ao WCF recursos para suportar melhor este tipo de cenário. Para isso, um novo conjunto de classes está sendo colocando dentro do .NET Framework, que representarão objetos JSON, algo que o Silverlight já possui há algum tempo, através de uma API chamada System.Json. Agregando isso no .NET Framework e, consequentemente, no WCF, podemos construir serviços que recebam e retornem objetos JSON, podendo combinar isso com variáveis do tipo dynamic, que também são suportadas pela linguagem.

É importante dizer que o que temos disponível atualmente, ainda está em fase de desenvolvimento, e o que verá aqui, poderá sofrer alguma alteração até a versão final. Isso está disponível no Codeplex, neste endereço. Várias novidades estão disponíveis, mas o foco neste artigo é apresentar a integração melhorada com JSON. Ao instalar este pacote, temos dois assemblies que compõem estas novas funcionalidades: Microsoft.Runtime.Serialization.Json.dll e Microsoft.ServiceModel.Web.jQuery.dll.

O primeiro assembly é onde temos o namespace System.Json, assim como no Silverlight. É dentro dele que estão todas as classes que servem para construir e representar objetos em formato JSON. A base para todas elas é a JsonValue, que define a estrutura de um objeto JSON dentro do .NET Framework, contendo também as funcionalidades comuns para todos os outros tipos JSON: JsonPrimitive, JsonObject e JsonArray. Todos herdam diretamente de JsonValue, implementando as funcionalidades específicas para cada uma delas.

Como sabemos, a representação JSON de um objeto nada mais é que um dicionário, e devido a isso, a classe JsonValue implementa a interface IEnumerable<KeyValuePair<string, JsonValue>>, para permitir a iteração dos valores que ela armazena internamente. JsonPrimitive, como o próprio nome diz, refere-se aos tipos primitivos suportados no JavaScript: String, Number e Boolean. Depois temos a classe JsonObject, que define uma coleção de JsonValue, e seu indexador é do tipo string (chave) e, finalmente, o JsonArray trata-se de uma coleção que armazena instâncias da classe JsonValue, e seu indexador é um número inteiro. Abaixo temos uma imagem que mostra a hierarquia destas classes:

No outro assembly, temos a implementação de objetos específicos que fazem parte da infraestrutura do WCF, criados para que o mesmo consiga facilmente receber e interprertar as requisições realizadas que recebem e retornam objetos específicos para este modelo. Entre as classes que fazem parte deste novo assembly, temos: WebHttpBehavior3, WebServiceHost3 e WebserviceHostFactory3, seguindo a mesma linha das classes que foram criadas quando o REST foi introduzido no WCF. Basicamente a necessidade destas classes é justamente permitir que o behavior WebHttpBehavior3 seja acoplado a execução, que efetua algumas validações na requisição para determinar se ela está de conformidade com os objetos suportados, e além disso, acopla também um tratador de erro específico, convertendo eventuais problemas em objetos do tipo JsonObject.

Para exemplificar, vamos criar um serviço que será responsável por armazenar uma lista de itens, que será manipulada por um formulário HTML, recorrendo ao jQuery para dialogar com o respectivo serviço. O detalhe mais importante a se notar, são os tipos envolvidos nos parâmetros e no retorno dos métodos, que como podemos perceber, fazem parte da nova API que vimos acima. Abaixo temos a implementação deste serviço:

[ServiceContract]
public class ServicoDeCache
{
    private List<JsonObject> itens = new List<JsonObject>();

    [WebInvoke]
    public void Adicionar(JsonObject item)
    {
        this.itens.Add(item);
    }

    [WebGet]
    public JsonObject Extrair(int codigo)
    {
        return 
            (
                from i in this.itens 
                where i.AsDynamic().Codigo.ReadAs<int>() == codigo 
                select i
            ).SingleOrDefault();
    }

    [WebGet]
    public JsonArray Recuperar()
    {
        return new JsonArray(this.itens.ToArray());
    }
}

Os métodos são autoexplicativos, e manipulam uma lista interna que serve como repositórios para os itens. O método Extrair é aquele que merece uma maior atenção. Ele recebe um inteiro como parâmetro e retorna a instância da classe JsonObject, recorrendo à “LINQ To JSON” para extrair o elemento solicitado pelo cliente. O que chama atenção também é o método AsDynamic, que é declarado na classe JsonValue, e como havia mencionado, converte a instância do objeto atual em dynamic, permitindo o acesso as propriedades criadas pelo cliente de forma dinâmica (late-bound). Depois temos o método genérico ReadAs<T>, que também está acessível através da classe JsonValue, que recorre ao serializador JSON para tentar extrair o valor tipado daquela propriedade específica.

Observação: Como podemos perceber, diferentemente de serviços que construímos até então, a classe que representa o serviço não implementa uma interface de contrato. Isso se deve ao fato de serviços REST não possuem um contrato definido, sem geração de metadados e sem operações (já que serviços REST são baseados em recursos). Agradeço aqui ao Carlos Figueira, membro do time de WCF, que me esclareceu essa questão.

Depois do serviço implementado, temos que nos preocupar em expor o serviço através do host correto, e como vimos acima, deverá ser através do WebServiceHost3. Existem algumas formas de fazer isso, e uma delas é utilizar o Url Routing para determinar que o host para este serviço deverá ser o WebServiceHost3. Isso já é o suficiente para deixar o serviço disponível para consumo, pois ele se encarrega de criar os endpoints necessários, sem ter necessidade de criar qualquer configuração no arquivo Web.config. O código abaixo deve ser declarado no arquivo Global.asax, para registrar a rota.

public class Global : HttpApplication
{
    private void Application_Start(object sender, EventArgs e)
    {
        this.RegisterRoutes();
    }

    private void RegisterRoutes()
    {
        RouteTable.Routes.Add(
            new ServiceRoute(
                “ServicoDeCache”, 
                new WebServiceHostFactory3(), 
                typeof(ServicoDeCache)));
    }
}

Finalmente, depois de toda essa configuração reliazada, chega o momento de consumir o serviço através de algum cliente. Poderia ser através do Fiddler, mas para exemplificar melhor, vamos utilizar o jQuery para invocar duas das operações que disponibilizamos acima: Adicionar e Recuperar.

http://scripts/jquery-1.4.2.min.js
http://scripts/json2.js

    function Adicionar() {
        var nome = $(‘#Nome’).val();
        var codigo = $(‘#Codigo’).val();
        var item = { Nome: nome, Codigo: JSON.parse(codigo) };

        $.ajax(
            {
                type: ‘POST’,
                url: ‘./ServicoDeCache/Adicionar’,
                dataType: ‘json’,
                contentType: ‘application/json’,
                data: JSON.stringify(item),
                processData: false,
                success:
                    function () {
                        alert(‘Item cadastrado com sucesso.’);
                    }
            });
    }

    function Recuperar() {
        $.ajax(
            {
                type: ‘GET’,
                url: ‘./ServicoDeCache/Recuperar’,
                dataType: ‘json’,
                contentType: ‘application/json’,
                data: null,
                processData: false,
                success:
                    function (itens) {
                        $(‘#Itens’).empty();

                        $.each(itens, function (indice, item) {
                            $(‘#Itens’)
                                .append($(”)
                                .attr(‘value’, item.Codigo)
                                .text(item.Nome));
                        });
                    }
            });
    }

O primeiro método constrói um objeto com duas propriedades: Codigo e Nome, que são abastecidas com informações do formulário, e em seguida, utilizamos a biblioteca JSON2 para transformá-lo em JSON. Já o método Recuperar retorna um array contendo os elementos que foram adicionados na coleção interna do serviço, recorrendo ao método $.each do jQuery para iterar pelos dados e exibí-los na tela. Se quiser saber mais sobre consumo de serviços através do jQuery, consulte este endereço.

Conclusão: Vimos no decorrer deste artigo algumas novidades que a Microsoft está adicionando ao WCF e ao .NET Framework, possibilitando serviços WCF se adequar melhor à serviços baseados REST e JSON, bem como facilitar o consumo destes mesmo serviços por aplicações .NET “tradicionais”.

Filtros para MessageLogging

Já mostrei neste artigo e vídeo como podemos fazer para habilitar o log de mensagens do WCF, onde podemos capturar o envelope da mensagem SOAP que está sendo trafegado entre as partes envolvidas.

Somente o fato de habilitar já é o suficiente para capturar todas as mensagens que chegam ao serviço e também aquelas que são devolvidas para os respectivos clientes, contendo o resultado do processamento da operação. Dependendo do tamanho do envelope e do volume de requisições, em pouco tempo, você pode consumir uma grande quantidade de espaço em disco.

Para melhorar isso, o message logging do WCF fornece filtros que podemos aplicar aos headers do envelope SOAP, e com isso, analisar e decidir se queremos ou não catalogar aquela mensagem. Para exemplificar, considere o contrato abaixo, que possui duas operações simples:

[ServiceContract(Namespace = “http://www.israelaece.com/servicos“)]
public interface IContrato
{
    [OperationContract]
    ItemDeExtrato[] VisualizarExtrato(string conta);

    [OperationContract]
    void Debitar(string conta, decimal valor);
}

A operação VisualizarExtrato retorna um array de itens que fazem parte de um extrato de uma determinada conta, informada através do parâmetro conta. Já a segunda operação, recebe a conta e o valor a ser debitado da mesma. Como sabemos, podemos ligar o logging do WCF para começarmos a capturar as mensagens que chegam para este serviço como um todo. O código abaixo ilustra essa configuração:

<?xml version=”1.0″ encoding=”utf-8″ ?>
<configuration>
  <system.diagnostics>
    <sources>
      <source name=”System.ServiceModel.MessageLogging”
              switchValue=”All”
              propagateActivity=”true”>
        <listeners>
          <add name=”Xml”
               type=”System.Diagnostics.XmlWriterTraceListener”
               initializeData=”C:TempMessageLogging.svclog” />
        </listeners>
      </source>
    </sources>
    <trace autoflush=”true” />
  </system.diagnostics>
  <system.serviceModel>
    <diagnostics>
      <messageLogging logEntireMessage=”true”
                      logMessagesAtServiceLevel=”true”
                      maxMessagesToLog=”100″
                      maxSizeOfMessageToLog=”200000″ />
    </diagnostics>
  </system.serviceModel>
</configuration>

Com isso, ao abrir o arquivo gerado pelo logging recém habilitado, veremos a mensagem que chegou para o serviço com a requisição de débito:

<s:Envelope xmlns:a=”http://www.w3.org/2005/08/addressing&#8221;
            xmlns:s=”http://www.w3.org/2003/05/soap-envelope”&gt;
  <s:Header>
    <Action a:mustUnderstand=”1″
            xmlns=”http://www.w3.org/2005/08/addressing&#8221;
            xmlns:a=”http://www.w3.org/2003/05/soap-envelope”&gt;http://www.israelaece.com/servicos/IContrato/Debitar</Action>
    <a:MessageID>urn:uuid:25eb06f3-b1d5-41a4-99c8-37f1a5905d7f</a:MessageID>
    <a:ReplyTo>
      <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address&gt;
    </a:ReplyTo>
    <a:To s:mustUnderstand=”1″>net.tcp://localhost:9392/srv</a:To>
  </s:Header>
  <s:Body>
    <Debitar xmlns=”http://www.israelaece.com/servicos”&gt;
      <conta>000001</conta>
      <valor>1200</valor>
    </Debitar>
  </s:Body>
</s:Envelope>

Mas o problema é que também as mensagens que chegam para a operação VisualizarExtrato também serão catalogadas, e como elas possuem uma grande quantidade de informações, neste caso, não temos a necessidade de monitorar essas mensagens. É aqui que entra em cena os filtros, tema deste artigo. Dentro do elemento messageLogging temos um sub-elemento chamado de filters, que nada mais é que uma coleção, onde você pode elencar diversos filtros, utilizando a linguagem/tecnologia XPath para analisar e escrever tais filtros. Exemplo:

<system.serviceModel>
  <diagnostics performanceCounters=”All”>
    <messageLogging logEntireMessage=”true”
                    logMessagesAtServiceLevel=”true”
                    logMessagesAtTransportLevel=”false”
                    maxMessagesToLog=”100″
                    maxSizeOfMessageToLog=”200000″>
      <filters>
        <add xmlns:a=”http://www.w3.org/2005/08/addressing&#8221;
             xmlns:s=”http://www.w3.org/2003/05/soap-envelope”>/s:Envelope/s:Header/a:Action%5Btext()=&#8221;http://www.israelaece.com/servicos/IContrato/Debitar“]</add>
      </filters>
    </messageLogging>
  </diagnostics>
</system.serviceModel>

Modificando o arquivo de configuração do serviço, estamos acrescentando um filtro que utilizaremos para catalogar as mensagens somente se a Action (que representa a operação) for igual a Debitar, descartando assim todas as mensagens que chegam para as outras operações e, consequentemente, eventuais dados gerados por elas.

Compartilhando recursos do ASP.NET entre proxies WCF

Quando hospedamos serviços WCF no IIS, podemos permitir ao serviço utilizar os recursos fornecidos pelo ASP.NET. Entre esses recursos, temos as variáveis de aplicação, de sessão, cookies, caching, etc. Para permitir isso, tudo o que precisamos fazer é habilitar através de um atributo na classe que representa o serviço, chamado de AspNetCompatibilityRequirementsAttribute. Ele fornece uma propriedade chamada RequirementsMode, que aceita uma das opções impostas pelo enumerador AspNetCompatibilityRequirementsMode. O código abaixo ilustra a sua configuração:

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class Servico01 : IServico01
{
    public string Ping(string value)
    {
        if (HttpContext.Current.Session[“Teste”] == null)
            HttpContext.Current.Session[“Teste”] = value;

        return HttpContext.Current.Session[“Teste”] as string;
    }
}

Note que na implementação estamos fazendo uso de variáveis de sessão. Além disso, precisamos ligar explicitamente essa compatibilidade com o ASP.NET no arquivo Web.config, que é utilizado pela aplicação que hospeda o serviço, e para isso, definimos o atributo aspNetCompatibilityEnabled do elemento serviceHostingEnvironment para True, conforme é mostrado abaixo:

<?xml version=”1.0″?>
<configuration>
  <system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled=”true” />
  </system.serviceModel>
</configuration>

E com essas configurações podemos de dentro de serviços WCF, utilizar as funcionalidades expostas pelo ASP.NET. É importante dizer que variáveis de sessão são controladas através de cookies, e mesmo em serviços WCF, eles continuam sendo utilizados para relacionar um determinado cliente e as suas respectivas variáveis de sessão que estão armazenadas no servidor.

Quando estamos publicando o nosso serviço através de um binding HTTP (BasicHttpBinding ou WSHttpBinding), eles possuem uma propriedade boleana chamada AllowCookies. O nome desta propriedade pode parecer confuso, pois a finalidade dela não é permitir que o binding utilize ou não cookies, mas sim determinar quem deverá manipulá-los.

Quando esta propriedade estiver definida como True (o padrão é False), o próprio runtime do WCF irá gerenciar os cookies. Isso quer dizer que ele criará uma instância da classe CookieContainer e a associará ao serviço, utilizando-a para todas as requisições subsequentes, ou seja, todo e qualquer cookie retornardo pelo serviço, será automaticamente reenviado nas futuras requisições para este mesmo serviço, sem a necessidade de controlar como isso é realizado.

O problema aparece quando temos mais do que um serviço dentro da aplicação (WebSite). Serviços diferentes, exigem proxies diferentes no cliente, e os cookies estão vinculado à um proxy específico, ou seja, se você utiliza dois proxies, sendo um para cada serviço (*.svc), os cookies não serão compartilhados, e com isso, os cookies e, consequentemente, variáveis de sessão, não serão compartilhados entre eles (serviços), gerando assim um resultado inesperado. A imagem abaixo ilustra a estrutura do lado do servidor, e como sabemos, existirá um proxy para cada serviço.

A implementação do Servico02 para o teste é extremamente simples, ou seja, ele apenas lê a informação de uma variável de sessão que foi criada pelo Servico01. Apesar deste serviço também estar configurado para exigir o modo de compatibilidade, isso não resolverá, já que o problema reside do lado do cliente, pois como ele não consegue manter o cookie entre os proxies, quando tentarmos executar este segundo serviço, o WCF e o ASP.NET entendem que não foi criada a sessão e, consequentemente, retornará nulo.

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class Servico02 : IServico02
{
    public string Ping(string value)
    {
        return HttpContext.Current.Session[“Teste”] as string;
    }
}

Para começar a resolver o problema, o primeiro passo é desligar o controle automático de cookies pelo WCF, definido a propriedade AllowCookies para False (que já é o padrão), assim como é mostrado abaixo. É importante dizer que essa propriedade deve estar devidamente configurada no cliente, que com isso, será responsável por gerenciar manualmente os cookies gerados pelo serviço.

<bindings>
    <basicHttpBinding>
        <binding allowCookies=”false” />
    </basicHttpBinding>
</bindings>

Depois dessa configuração, o resto será através do próprio código. Se notarmos no exemplo abaixo, podemos perceber que temos os dois proxies, sendo um para cada serviço. Temos também uma string que receberá o valor do cookie gerado, que corresponderá ao Id da sessão. Logo depois da criação do proxy para o primeiro serviço, instanciamos a classe OperationContextScope, que cria um bloco de acesso ao contexto da operação, tendo acesso a recursos como headers e properties do serviço. Já dentro deste bloco, utilizamos a classe HttpResponseMessageProperty para extrairmos os valores pertinentes a resposta da execução do método Ping. Note que ela está sendo acessada depois da chamada ao método Ping. Com a coleção de headers em mãos, utilizamos o header SetCookie para extrair o valor do cookie de sessão do ASP.NET (ASP.NET_SessionId) e guardamos na string sessionIdCookie.

Se ainda invocarmos o método Ping a partir do mesmo proxy, não teremos problema, ou seja, ele conseguirá manter a variável de sessão. Mas aqui a situação é outra, ou seja, temos um segundo proxy para o consumo de um outro serviço, mas que queremos utilizar a mesma variável de sessão, gerada anteriormente. Para o segundo proxy, nós fazemos o processo inverso, ou seja, criamos o escopo de acesso aos recursos da requisição, e construímos a instância da classe HttpRequestMessageProperty e configuramos o cookie com aquele valor que salvamos depois de invocar o método através do primeiro proxy.

static void InvocarServico()
{
    string sessionIdCookie = null;

    using (Servico01Client proxy1 = new Servico01Client())
    {
        using (new OperationContextScope(proxy1.InnerChannel))
        {
            Console.WriteLine(proxy1.Ping(“teste”));

            HttpResponseMessageProperty response =
                OperationContext.Current.IncomingMessageProperties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty;

            sessionIdCookie = response.Headers[HttpResponseHeader.SetCookie];
        }
    }

    using (Servico02Client proxy2 = new Servico02Client())
    {
        using (new OperationContextScope(proxy2.InnerChannel))
        {
            if (!string.IsNullOrWhiteSpace(sessionIdCookie))
            {
                HttpRequestMessageProperty request = new HttpRequestMessageProperty();
                request.Headers[HttpRequestHeader.Cookie] = sessionIdCookie;

                OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = request;
            }

            Console.WriteLine(proxy2.Ping(“outro valor”));
    }
}

Com isso tudo criado, agora o método Ping do proxy2 será capaz de retornar o valor gerado e armazenado na variável de sessão através do proxy1. Caso você não utilize essa técnica, a requisição para os métodos do proxy2 serão enviadas sem o cookie, e com isso, o WCF e o ASP.NET não saberá que existe variáveis de sessão já criadas para aquele usuário, e com isso, não terá acesso as informações previamente criadas.

Utilizando async/await com proxies WCF

Como comentei anteriormente, a Microsoft está fazendo grandes melhorias para a próxima versão do C#, com o intuito de facilitar a programação assíncrona dentro do .NET Framework. No artigo mencionado, lá temos como proceder para invocar um código de forma assíncrona neste novo modelo, que é através da utilização da keyword async no método que deverá ser invocado de forma assíncrona, e dentro dele, utilizamos a keyword await, para dizer que a região a seguir trata-se de um callback.

Como sabemos, os delegates já trazem nativamente suporte para que eles sejam invocados de forma síncrona ou assíncrona. Mas como o modelo de programação muda partir destas novas funcionalidades que estão sendo criadas no C#, ou seja, passaremos a utilizar a classe Task<T> para representar o processo assíncrono, então podemos recorrer a um método chamado FromAsync da classe TaskFactory, que foi incluído no .NET Framework a partir da versão 4.0. Fazendo uso deste método, podemos invocar um delegate de forma assíncrona utilizando o seguinte código:

Func<string, string> ping = s => s + ” ping!”;

var task =
    Task.Factory.FromAsync<string, string>(
        ping.BeginInvoke, ping.EndInvoke, “Teste”, null);

Como podemos perceber no código acima, informamos para o método FromAsync o par de métodos Begin/End que compõem a chamada assíncrona para uma tarefa, dando origem assim à uma instância da classe Task, que daqui em diante, podemos utilizar em conjunto com o novo modelo que está sendo proposto pela Microsoft.

Mas como podemos utilizar esta técnica para consumir serviços WCF? Como já comentei neste artigo, podemos optar durante a referência para o serviço na aplicação cliente (Add Service Reference), por marcar a opção “Generate asynchronous operations”, e com isso, para cada operação existente no serviço, serão criadas três métodos, sendo um para a chamada síncrona, e os outros dois irão compor a chamada assíncrona (BeginNomDaOperacao/EndNomeDaOperacao). Sabendo que há como transformar o modelo de Begin/End em Tasks, poderíamos utilizar o código acima para consumir o serviço WCF de forma assíncrona e recorrer ao novo modelo. Mas ao invés de termos todo esse trabalho, já há uma forma de fazer com que o proxy também seja gerado com as opções de Tasks.

Para isso, utilizamos uma implementação que a Microsoft criou chamada de TaskAsyncWsdlImportExtension. Essa classe intercepta a referência do serviço na aplicação cliente, e cria a versão assíncrona baseando-se em Tasks para todas as operações que ele encontra no documento WSDL. Para isso funcionar, devemos referenciar a DLL que consta essa classe no projeto cliente (ela está incluída nos exemplos do CTP). Depois disso, devemos modificar o arquivo de configuração para definirmos essa estensão, assim como é mostrado através do código abaixo:

<?xml version=”1.0″ encoding=”utf-8″ ?>
<configuration>
 <system.serviceModel>
  <client>
   <metadata>
    <wsdlImporters>
     <extension type=”TaskWsdlImportExtension.TaskAsyncWsdlImportExtension, TaskWsdlImportExtension…” />
    </wsdlImporters>
   </metadata>
  </client>
 </system.serviceModel>
</configuration>

Em seguida, devemos efetuar a referência para o serviço e marcar a opção para a geração das operações assíncronas. Isso já é o suficiente para a classe TaskAsyncWsdlImportExtension entrar em ação e construir a nova versão deste método.

public partial class ContratoClient : ClientBase<IContrato>, IContrato
{
    public Task<string> PingAsync(string value)
    {
        return Task<string>.Factory.FromAsync(
            new Func<string, AsyncCallback, object, IAsyncResult>(((IContrato)(this)).BeginPing),
            new Func<IAsyncResult, string>(((IContrato)(this)).EndPing), value, null);
    }
}

A partir de agora, podemos efetivamente recorrer ao novo modelo de programação assíncrona do C#, ou seja, com a instância da classe Task em mãos, podemos fazer uso dela dentro de métodos marcados como async e, consequentemente, utilizar a keyword await para determinar o que queremos fazer com o resultado quando ele for devolvido. Note que o código fica bem mais sucinto quando comparado ao modelo Begin/End que fazemos atualmente.

private async static void InvocarServico()
{
    var temp = await proxy.PingAsync(“Algum Valor”);
    Console.WriteLine(“O resultado foi: {0}”, temp);
}

O Framework possibilita, a Linguagem facilita

Desde a primeira versão do .NET Framework, várias funcionalidades foram construídas e as linguagens (aqui C# e VB.nET) facilitam o acesso as mesmas, tornando a vida do desenvolvedor muito mais simples. Para mostrar um exemplo simples, podemos perceber que grande parte de nós não utiliza o tipo Int32 para declarar um inteiro. Optamos pela keyword int. Grande parte de nós não utiliza o tipo Boolean para declarar um valor boleano. Optamos pela keyword bool.

Já no .NET Framework 2.0, a Microsoft introduziu no C# os iteradores, que evita a escrita explícita de classes que implementam as interfaces IEnumerable<T> e IEnumerator<T>. Tudo o que ela fez foi criar uma keyword chamada yield, que elimina toda essa necessidade, abstraindo a “complexidade” que esta implementação possui.

Para ter um exemplo mais recente, podemos avaliar as expressões lambdas. Elas nada mais são do que uma forma muito mais simples de se escrever (não de entender) delegates. Salvo a sintaxe de cada linguagem, a Microsoft está tornando tarefas complexas de serem realizadas em um modo bem mais simples de se fazer, ou seja, com novas essas keywords e um jeito diferente de se escrever o mesmo código, grande parte do trabalho complexo agora fica sob responsabilidade do compilador, que avalia e gera a saída necessária para tudo isso funcionar.

Entre as tarefas mais complexas que existem dentro do .NET Framework hoje, é a programação assíncrona. Ela não é nada trivial, pois exige um conhecimento razoável de como as coisas funcionam nos bastidores, o uso do par de métodos BeginXXX/EndXXX, das formas de capturar o resultado (callback ou poll), exceções que são disparadas, etc. Devido a essa complexidade toda, a programação assíncrona foi o alvo da Microsoft para tornar essa tarefa bem mais amena.

Apesar de isso já correr há algum tempo nos bastidores, a Microsoft anunciou hoje no PDC, um novo modelo de programação assíncrona, que permite a escrita do código assíncrono de forma tão simples quanto ao código síncrono. A partir de agora, tudo o que precisamos fazer é declarar a função como sendo assíncrona, e isso é feito através de uma nova keyword chamada async. Além disso, a função também poderá retornar um tipo predefinido que é a instância da classe Task<T>, que como o nome já sugere, representa uma tarefa que está sendo executada.

public async Task<decimal> CalcularSalarioAsync(string empregado)
{
    var resultado = await new ServicoDeSalario().CalcularAsync(empregado);

    return resultado;
}

Analisando o código acima, podemos perceber que também surgiu uma nova keyword: await. Apesar de parecer que ela irá bloquear a thread até que seja finalizado, não é isso o que acontece. Na verdade, quando o compilador encontrar essa keyword, o que ele fará é determinar que o que virá dali para baixo, até o final do método, será o callback que deverá ser disparado quando o processo assíncrono for finalizado.

A finalidade de facilitar isso, é fazer com que a programação assíncrona dentro do .NET Framework seja tão simples quanto a programação síncrona e, consequentemente, tornar as aplicações mais interativas quando falamos daquelas que exigem muitas interações de UI, ou tornando mais simples quando precisamos escrever alguma funcionalidade de forma assíncrona do lado do servidor, como é o caso de páginas/controllers assíncronos do ASP.NET e serviços em geral.

Explorarei mais detalhadamente essa funcionalidade em futuros artigos. Por agora, foi apenas um overview do que está por vir, e também mostrar que algumas complexidades são, aos poucos, absorvidas pelas linguagens. Da mesma forma que as facilidades anteriores que foram criadas dentro das linguagens, esta também será incorporada, mas vale ressaltar que continua sendo importante o seu entendimento interno da mesma forma que é importante conhecer delegates, caso contrário, expressões lambdas serão completamente estranhas.