Melhorias na configuração de serviços WCF

O schema de configuração do WCF é um pouco complexo, e mesmo utilizando o Microsoft Service Configuration Editor que, de forma gráfica, auxilia na configuração do serviço, há muito o que ser feito, já que existem várias propriedades, endpoints, bindings, behaviors, metadados, etc., para serem configurados. Apesar de complexo, depois que você entende o básico, até fica um pouco mais simples, mas sempre irá espantar os iniciantes da tecnologia, principalmente quando comparado com o ASP.NET Web Services (ASMX), que bastava criar o projeto, o serviço, compilar e referenciá-lo nos clientes. 

Uma das grandes novidades no WCF 4.0, é justamente a configuração de serviços de forma muito mais simplificada. Na verdade, ele não obrigará mais fazer qualquer configuração. Basta criar o ServiceHost, informar o tipo da classe que representa o serviço e o endereço onde quer disponibilizar o serviço. Sendo assim, o código abaixo funcionará sem qualquer configuração no arquivo App.config ou Web.config:

using (ServiceHost host = new ServiceHost(typeof(ServicoDeUsuarios),
    new Uri[]{ new Uri(“http://localhost:7373/srv”), new Uri(“net.tcp://localhost:7373/srv”) }))
{
    host.Open();
    Console.ReadLine();
}

Se executarmos o código acima, o serviço levantará sem problemas. Caso a classe que representa o serviço implementar 2 contratos diferentes, 2 endpoints para cada baseAddress. É importante dizer que o WCF torna as configurações não obrigatórias pelo cliente, mas isso não quer dizer que ele não precise mais delas. É justamente nesse momento que entra em cena o Protocol Mapping. O WCF 4.0 inclui uma nova seção no arquivo machine.config chamada <protocolMapping />, assim como vemos abaixo:

<protocolMapping>
  <add scheme="http" binding="basicHttpBinding"/>
  <add scheme="net.tcp" binding="netTcpBinding"/>
  <add scheme="net.pipe" binding="netNamedPipeBinding"/>
  <add scheme="net.msmq" binding="netMsmqBinding"/>
</protocolMapping>

Como podemos notar, temos uma coleção de schemes suportados pelo WCF, onde ele irá mapear cada scheme para um binding específico. Isso quer dizer que, quando você omitir as configurações na sua aplicação, ele irá extraí-las a partir deste mapeamento, baseando-se nos endereços (scheme) que você está utilizando para expor o serviço. Assim como grande parte das configurações do .NET, podemos também customizar esse mapeamento, tanto em nível de máquina (para que todos os serviços que rodem naquela máquina sigam aquela configuração), quanto em nível de serviço/aplicação. Para exemplificar a mudança, você poderia dizer que todos os serviços que rodarem através do protocolo HTTP, utilizassem o binding WSHttpBinding ao invés do BasicHttpBinding, utilizando segurança em nível de mensagem:

<protocolMapping>
  <clear scheme="http" />
  <add scheme="http" binding="wsHttpBinding" bindingConfiguration="messageSecurity" />
</protocolMapping>

<bindings>
  <wsHttpBinding>
    <binding name="messageSecurity">
        <security mode="Message" />
    </binding>
  </wsHttpBinding>
</bindings> 

No atributo binding do elemento add que vimos acima, definimos o nome do binding que será utilizado por aquele scheme e, opcionalmente, podemos definir também o atributo bindingConfiguration, apontando para uma seção dentro do arquivo de configuração, customizando as características básicas daquele binding. De forma semelhante trabalham os behaviors, ou seja, os behaviors sem o atributo name definido, serão automaticamente adicionados à coleção de behaviors de qualquer binding.

Ainda falando sobre as configurações, todos sabemos que os endpoints possuem três características, conhecidas também como ABC: Address, Binding e Contract (mais detalhes neste artigo). Além disso, ainda temos os behaviors que são configurações adicionais ao serviço, e que refletem diretamente na execução do mesmo. Como essas características são altamente configuráveis, talvez tenhamos a necessidade de disponibilizar esse “conjunto” para várias outras aplicações, precisando de uma “reutilização” da configuração destes endpoints.

Pensando nisso, a Microsoft também criou os Standard Endpoints. Assim como o Protocol Mapping, os Standard Endpoints são implementados através de uma seção no arquivo de configuração chamada <standardEndpoints />. Esta seção nada mais é do que uma coleção, onde cada elemento dentro dela é representado pelo sub-elmento chamado <standardEndpoint />, que receberá todas as configurações de um endpoint específico. E, finalmente, para relacionar essas configurações à um endpoint, utilizamos um novo atributo do elemento <endpoint />, chamado de kind, onde devemos informar o nome de um standard endpoint, assim como é mostrado abaixo:

<system.serviceModel>
    <services>
        <service name="ServicoDeUsuarios">
            <endpoint kind="webHttpEndpoint" />
        </service>
    </services>
</system.serviceModel>

Quando utilizamos um projeto WCF Service ou até mesmo um projeto ASP.NET, é comum adicionarmos arquivos *.svc, onde cada um deles representa um serviço específico. Como já sabemos, estes arquivos possuem apenas uma única linha, que é a diretiva @ServiceHost, que define a classe do CodeBehind, o host, e servirá como alvo da requisição, ou seja, se ele não existir fisicamente, temos um erro 404, de recurso não encontrado.

Para facilitar a distribuição e gerenciamento, não há mais a necessidade deles. Ao invés disso, listamos os serviços diretamente no arquivo de configuração, neste caso, o Web.config, utilizando o já conhecido elemento serviceHostingEnvironment. Agora, dentro dele, temos um sub-elemento chamado serviceActivations. Ele é uma coleção, onde podemos elencar todos os serviços:

<system.serviceModel>
    <serviceHostingEnvironment>     
      <serviceActivations>
        <add relativeAddress="~/ServicoDeUsuarios.svc" service="ServicoDeUsuarios"/>
      </serviceActivations>
    </serviceHostingEnvironment>
</system.serviceModel>

É importante dizer que essas – novas – funcionalidades fazem parte do Beta 1. Talvez isso ainda mude até a versão final do produto. As novidades também não param por aí. O WCF 4.0, além do que foi visto aqui, ainda traz muitas melhorias em diversas outras áreas, que abordarei em futuros artigos.

Resumo da classe OperationContext

Assim como o COM+ e o ASP.NET, o WCF também disponibiliza uma classe com membros que trazem informações contextuais, ou seja, a respeito da requisição corrente. Essa classe chama-se OperationContext. O runtime do WCF cria e disponibiliza através da propriedade estática Current, a instância desta classe representando as informações referentes a requisição atual, podendo ser utilizada do lado do serviço como no cliente.

Essa classe é utilizada em grande parte do WCF, tanto pela sua própria infraestrutura como pelo desenvolvedor que cria o serviço ou o cliente. Grande parte dessas propriedades já foram abordados por aqui, mas em momentos específicos. No artigo de segurança, foi falado sobre a propriedade ServiceSecurityContext, que retorna um objeto com o mesmo nome, representando o contexto de segurança do cliente (podendo ser o serviço ou o cliente (proxy)).

Outra propriedade importante que essa classe expõe é a SessionId. Essa propriedade retorna uma GUID que identifica a sessão atual, que é útil para correlação de mensagens, logs, etc. Para mais informações sobre sessões do WCF, consulte este artigo. Há também uma propriedade chamada Host, que como o próprio nome diz, retorna a instância da classe ServiceHostBase que hospeda e gerencia a execução do serviço.

Temos também quatro propriedades nesta classe que trabalham com headers e properties: IncomingMessageHeaders, IncomingMessageProperties, OutgoingMessageHeaders e OutgoingMessageProperties. Como utilizá-la e para entender a direferença entre esses dois tipos, consulte este artigo.

Recentemente também falei aqui sobre extensibilidade de uma mensagem. Como a classe OperationContext também implementa a interface IExtensibleObject<T>, definindo o tipo T como OperationContext, é possível adicionarmos à ela extensões com um tempo de vida mais curto, “vivendo” apenas durante a execução da requisição corrente.

A propriedade InstanceContext, fornece informações a respeito da instância da classe que representa o serviço, ou seja, aquela classe onde implementamos o contrato e a definimos no construtor do ServiceHost. Temos também a propriedade RequestContext, que expoõe a instância da classe RequestContext. Esta classe é a relação entre a mensagem da requisição e a mensagem da resposta. Esta propriedade pode ser nula quando estiver trabalhando com operações do tipo one-way.

Para finalizar, temos dois métodos: SetTransactionComplete e GetCallbackChannel<T>. O primeiro método é responsável por efetuar o commit de uma transação (caso ela exista). Para maiores informações sobre como utilizá-lo, consulte este artigo, que aborda como funcionam as transações dentro de serviços WCF. O método GetCallbackChannel<T> é responsável por estabelecer a comunicação entre o serviço e o cliente, utilizando contratos de callback.

WCF – Internals e Extensibilidade

O Windows Communication Foundation traz nativamente diversas funcionalidades, quais podemos combiná-las e utilizá-las para tornar o serviço mais poderoso. A arquitetura que a Microsoft criou para suportar as diferentes (talvez até distintas) características dos serviços é altamente extensível, o que nos permite acoplarmos um determinado código em algum ponto predefinido, durante a execução do serviço. As interceptações podem ser realizadas em diferentes momentos da execução, podendo ser realizada do lado do cliente ou do serviço. A finalidade deste artigo é exibir alguns detalhes internos da arquitetura da WCF e, como foco principal, conhecer os pontos de extensibilidade que ele nos fornece.

Internals

Muitas vezes, quando queremos criar e consumir um serviço, basicamente criamos o contrato, o implementamos e, finalmente, hospedamos este serviço em algum host conhecido (para saber mais sobre hosting de serviços WCF, consulte este artigo). Quando o serviço está em execução, basta referenciá-lo em uma aplicação cliente e consumi-lo. Este é um processo relativamente simples mas que, internamente, há vários elementos (classes) que fazem tudo isso funcionar corretamente. Sendo assim, antes de efetivamente começarmos a discutir as formas de extensibilidade que temos dentro do WCF, devemos compreender a finalidade de alguns dessem elementos que compõem o runtime do WCF. Com a imagem abaixo, conseguimos visualizar graficamente como ocorre a requisição à um serviço WCF.

Figura 1 – Processo de execução de um serviço WCF.

Na imagem acima temos do lado esquerdo o cliente e do lado direito o serviço. Na imagem de exemplo, o cliente (remetente) está enviando a mensagem para o serviço (destinatário). Aqui é importante ressaltar que o eles possuem uma certa simetria, ou seja, quando o serviço envia a resposta, então ele passa a ser o remetente e o cliente o destinatário da mensagem. Dentro do runtime do WCF a mensagem é representada pela classe Message, contida no namespace System.ServiceModel.Channels, e é responsável por abstrair a mensagem SOAP, fornecendo membros de acesso as informações contidas neste envelope.

O primeiro elemento e o mais conhecido é o proxy. Quando efetuamos a referência para um serviço através da IDE do Visual Studio .NET (ou através do utilitário svcutil.exe), automaticamente ele extrai as informações do documento WSDL exposto pelo serviço, e cria uma classe com as mesmas operações definidas pelo contrato do serviço. Apesar de parecer uma classe normal, ela herda da classe ClientBase<TChannel> qual tem a finalidade de abstrair vários detalhes complexos da comunicação, tornando o consumo do serviço extremamente fácil, como se fosse uma chamada para um método local. Quando um dos métodos do proxy é invocado, ele delega a execução para uma classe chamada ChannelFactory<TChannel>, que por sua vez, é responsável por criar e gerenciar a infraestrutura necessária para que o cliente envie informações para um serviço ou, de forma mais precisa, para um endpoint.

O próximo elemento que temos na imagem é a Channel Stack e, como podemos notar, ela está presente em ambos os lados. Cada channel dentro desta stack é responsável por uma tarefa distinta, como por exemplo: a codificação dos caracteres, a conversão com o protocolo de comunicação, e o suporte às implementações dos protocolos WS-*, como é o caso de transações, mensagens confiáveis, segurança, etc. Os elementos que irão compor a Channel Stack dependerá da configuração do binding que está sendo utilizado. É através deles que “ligamos” ou “desligamos” os recursos que temos a nossa disposição. Os únicos itens que sempre irão existir são o Encoder e o Transport, responsáveis pela codificação e pelo transporte, respectivamente.

Quando o proxy gerar a mensagem (representada pela classe Message), ele a encaminhará para a Channel Stack que, por sua vez, passará por cada um dos itens ali contidos, configurando esta mesma mensagem com suas respectivas funcionalidades. Deste lado do processo, o Encoder é responsável por transformar a classe Message em bytes e, em seguida, encaminhar o resultado para o channel responsável pela conversação com o protocolo. Do lado do serviço, ao receber a mensagem, a execução dos itens acontecem em ordem inversa, passando inicialmente pelo channel do transporte que recupera os bytes e os encaminham para o Encoder agrupá-los e transformar em uma instância da classe Message.

Por último, e não menos importante, temos o Dispatcher, que está do lado do serviço. Quando criamos um host (ServiceHost), definimos as URIs para o serviço. Cada uma dessas URIs terá seu próprio listener, que é representado pela classe ChannelDispatcher. Quando uma mensagem chegar, o ChannelDispatcher interroga cada um dos objetos EndpointDispatcher (falaremos dele mais abaixo) que estão associados a ele, verificando se cada endpoint pode ou não aceitar a mensagem, passando-a para um deles. Na maioria das vezes, o ChannelDispatcher terá apenas um único EndpointDispatcher associado, mas há alguns casos em que podemos ter mais do que um.

Já o EndpointDispatcher expõe algumas informações e pontos de customização relacionados ao endpoint, que podemos utilizá-los para controlar o processo de recebimento das mensagens. Ele fornece duas propriedades chamadas AddressFilter e ContractFilter, que trabalham em conjunto para assegurar que a mensagem recebida será encaminhada para o método correto dentro da instância da classe que representa o serviço. Uma outra propriedade importante desta classe é a DispatchRuntime. Ela retorna uma instância da classe DispatchRuntime que além de ter vários pontos para customização, é responsável por selecionar o método a ser invocado no objeto que representa o serviço, serializar e deserializar os parâmetros para o método, e ainda gerenciar o tempo de vida deste objeto. A imagem abaixo exibe o relacionamento entre as classes que falamos agora:

Figura 2Dispatchers.

A classe DispatchRuntime ainda possui duas propriedades que trabalham em conjunto: OperationSelector e Operations. A primeira delas, retorna a instância de uma classe que implementa a Interface IDispatchOperationSelector, que possui apenas um único método chamado SelectOperation, recebendo a mensagem atual (através da classe Message) como parâmetro e retornando uma string que representa o nome do método requisitado, e será utilizada para procurar a instância da classe DispatchOperation dentro do dicionário, armazenado na propriedade Operations. Uma vez que o método requisitado é encontrado dentro deste dicionário, uma instância da classe DispatchOperation é retornada, sendo ela a responsável por deserializar os parâmetros da mensagem, invocar o método e, finalmente, serializar o resultado na mensagem de retorno.

A arquitetura do WCF possui inúmeros pontos de extensibilidade, onde podemos acoplar um código customizado para interceptar um determinado momento. É importante dizer que nem todos esses pontos serão abordados neste artigo, justamente porque exigem uma abordagem diferenciada, como é a caso da customização da autenticação e autorização, que antes de falar efetivamente sobre essa possibilidade, devemos ter uma introdução sobre a infraestrutura de segurança fornecida pelo WCF. O artigo irá se limitar em abordar os pontos de interceptação referentes à manipulação da mensagem, suas operações e parâmetros. Mais adiante, ainda neste artigo, também iremos analisar como podemos utilizar algumas extensões para o compartilhamento de dados no proxy ou no dispatcher.

Extensibilidade da Mensagem

A extensibilidade é uma característica do WCF. Ele fornece vários pontos em que podemos injetar um código customizado, para conseguir interceptar a execução do serviço. É importante dizer que tanto o proxy quanto o dispatcher fornecem vários pontos de extensibilidade, permitindo que a customização seja feita em ambos os lados. Para ilustrar onde podemos acomodar o código customizado, vamos analisar separadamente cada um dos lados da comunicação, exibindo o local da inserção e também a finalidade, ou melhor, o que você poderá fazer naquele “contexto”. Para iniciar, vamos analisar a parte do cliente, responsável por invocar um método do serviço:

Figura 3 – Pontos de extensão do lado do cliente.

Como podemos notar, para cada operação que invocamos, um objeto do tipo ClientOperation é criado. Este objeto tem a finalidade de criar um ponto para modificar ou estender a execução de uma operação específica de um determinado contrato. Depois dele, a mensagem é encaminhada para a próxima classe, que a ClientRuntime que, assim com a anterior, possui pontos de extensão para inserir um código customizado para todas as mensagens que são enviadas e/ou recebidas pela aplicação cliente.

Os pontos de extensibilidade deste lado estão em vermelho. O primeiro deles (1) é o inspetor de parâmetros que, como o próprio nome diz, nos permite analisar os valores dos parâmetros de uma determinada operação, que estão sendo enviados pelo serviço. Isso nos permitirá inserir algum código para validar se o valor está ou não em um formato correto, seguindo alguma expressão regular, etc. Já no segundo ponto (2), temos a possibilidade de interceptar a “serialização” e formatação da mensagem, utilizando as informações que tem até o momento para criar a e abastecer as informações em uma instância da classe Message. Finalmente, temos o terceiro e último ponto de extensão que é inspeção da mensagem, sendo o último estágio em que você conseguirá analisar a mensagem antes dela ser enviada à Channel Stack. Já do lado do serviço temos:

Figura 4 – Pontos de extensão do lado do serviço.

Como falamos acima, o cliente e o serviço possuem uma certa simetria e podemos notar isso novamente na comparação dessas duas imagens. Note que do lado do serviço os processos acontecem em ordem inversa, começando pela Channel Stack devolvendo a instância da classe Message para o DispatchRuntime (já falamos sobre ela acima). Nesta classe possuímos dois pontos para injetarmos nosso código. O primeiro deles (1) é a inspeção da mensagem que, como do lado do cliente, podemos utilizar para efetuar alguma análise da mensagem que chegou ao serviço. Na sequência, temos a seleção da operação (2), onde é o momento do dispatcher escolher o método do serviço a ser executado, baseando-se na mensagem enviada pelo cliente. Depois disso, temos o objeto DispatchOperation, qual é criado um por operação e fornecerá outros três pontos de extensibilidade, sendo a possibilidade de interceptar a “deserialização” da mensagem (3) e a inspeção dos parâmetros (4), assim como ocorre no cliente. Finalmente, e não menos importante, ainda podemos customizar o disparo da operação solicitada pelo cliente (5).

Como podemos notar, cada um dos pontos de extensibilidade estão ligados à alguma objeto que faz parte do runtime do WCF. Para injetar um código customizado em algum destes pontos que vimos nas imagens acima, é necessário criar algumas classes e implementar algumas interfaces fornecidas pelo próprio WCF. Cada uma dessas interfaces disponibilizará métodos e propriedades que, quando adicionadas à execução, invocará o código ali definido. Para cada um dos estágios de interceptação, há uma interface específica que está dentro do namespace System.ServiceModel.Dispatcher, sendo:

Interface Descrição
IParameterInspector

Interface que deve ser implementada para a criação customizada para inspecionar ou modificar os parâmetros antes ou depois da chamada à operação. Isso permitirá efetuarmos alguma validação antes de efetivamente executar a chamada. Do lado do cliente, esse inspetor é chamado antes da requisição ser serializada e enviada ao serviço e também invocado após a resposta ser deserializada mas antes dos valores de retorno chegar até o proxy. Instâncias das classes que implementam esta interface devem ser adicionadas à coleção de ParameterInspectors, que está acessível a partir das classes DispatchOperation e ClientOperation.

IOperationInvoker

Esta interface nos permitirá interceptar a execução da operação, disponibilizando um método Invoke e passando à ele a instância do objeto que representa o serviço, um array de parâmetros de entrada que foram extraídos da mensagem e um array de parâmetros para representar o resultado. Também há dois métodos auxiliares chamados InvokeBegin e InvokeEnd, que correspondem à chamada assíncrona. Instância da classe que implementa esta interface deve ser adicionada à propriedade Invoker da classe DispatchOperation.

IDispatchOperationSelector

Baseando-se na mensagem, permite criarmos uma estrutura para interceptar a escolha da operação, retornando o nome da operação que o runtime deverá invocar no serviço. Instância da classe que implementa esta interface deve ser adicionada à propriedade Selector da classe DispatchRuntime.

IClientOperationSelector

Quando uma instância da classe que implementa esta interface é adicionada ao runtime, possibilitará interceptar qual dos métodos do proxy será invocado, podendo até modificar o nome do método que a aplicação (cliente) requisitou.

IDispatchMessageInspector

Esta interface fornece métodos para inspecionar ou modificar uma mensagem antes de executar a operação ou antes de retornar a resposta para o chamador. Instâncias das classes que implementam esta interface devem ser adicionadas à coleção de MessageInspectors, que está acessível a partir da classe DispatchRuntime.

IClientMessageInspector

Da mesma forma que a anterior, esta interface permite inspecionar ou modificar uma mensagem antes de enviar a requisição para o serviço ou depois de receber a resposta dele. Instâncias das classes que implementam esta interface devem ser adicionadas à coleção de MessageInspectors, que está acessível a partir da classe ClientRuntime.

IDispatchMessageFormatter

Esta interface permite criar uma extensão do formatador, controlando a serialização dos parâmetros na mensagem e também a deserialização da mensagem em parâmetros da aplicação.

IClientMessageFormatter

Também permite criar uma extensão do formatador, interceptando a serialização e deserialização dos objetos em mensagens e vice-versa.

Depois de analisado cada uma das interfaces que temos à nossa disposição para extensibilidade, então é o momento de analisarmos os códigos correspondentes à cada uma delas. Por questões de espaço, irei omitir essas implementações no artigo, e colocarei apenas os comentários correspondentes a cada interface aqui. Antes de iniciar, é importante que você abra o código fonte vinculado a este artigo para que possa acompanhar as explicações. Para facilitar, a imagem abaixo exibe a estrutura do projeto de exemplo e também cada uma das customizações que foram realizadas e serão comentadas a seguir:

Figura 5 – Estrutura das extensões criadas.

Começando de baixo para cima, vamos falar sobre a classe ParameterInspector. Como a finalidade é inspecionar os parâmetros e também o valor de retorno, ela traz dois métodos: BeforeCall e AfterCall. O primeiro deles, recebe como parâmetro o nome do método que será invocado e um array de objetos representando os parâmetros de entrada (isso será vazio quando o método não tiver parâmetros). Além destes parâmetros, este método retorna um object que falarei a seguir. Já o método AfterCall, possui em sua assinatura o nome do método, um array de objetos de saída, um object representando o resultado da operação (será nulo quando a mesma for void) e, finalmente, um object chamado correlationState, que devolverá o objeto retornado pelo método BeforeCall para correlacionar as chamadas.

Como já dissemos acima, o OperationSelector é o responsável por selecionar a operação (método) que será invocado no serviço ou no cliente e, justamente por isso, essa classe implementa as interfaces IDispatchOperationSelector e IClientOperationSelector. A primeira delas disponibiliza um método chamada SelectOperation, enviando a instância da mensagem corrente e retornando uma string, representando o método que será invocado no serviço. Já do lado do cliente, a interface IClientOperationSelector possibilita interceptar qual dos métodos do proxy que está sendo invocado. Essa interface também possui um método chamado SelectOperation, mas com uma assinatura diferente, ou seja, recebe uma instância da classe MethodBase, indicando o método solicitado pela aplicação e também um array de objetos que representa os parâmetros para o método solicitado. Esse metódo também retorna uma string que representa o método (operação) do proxy a ser executado.

A classe OperationInvoker implementa a interface IOperationInvoker, fornecendo métodos para a chamada síncrona (Invoke) ou assíncrona (InvokeBegin e InvokeEnd). Quem determinará qual dos métodos será invocado é a propriedade boleana chamada IsSynchronous que já é autoexplicativa. O método Invoke recebe como parâmetro a instância do objeto que representa o serviço, os parâmetros de entrada e saída, retornando um objeto que representa o resultado do método invocado.

MessageInspector tem a responsabilidade de inspecionar as mensagens tanto do lado do serviço quanto do cliente e, justamente por isso, implementa as interfaces IDispatchMessageInspector e IClientMessageInspector. A primeira interface disponibiliza dois metódos: AfterReceiveRequest e BeforeSendReply. Como o próprio nome diz, o primeiro método é disparado depois que o serviço recebeu a requisição do cliente; já o segundo, é disparado antes de enviar o resultado para o cliente que solicitou. A interface IClientMessageInspector também traz dois métodos: AfterReceiveReply e BeforeSendRequest, sendo o primeiro método disparado depois de receber a resposta do serviço, e o segundo antes de enviar a requisição para o serviço.

Finalmente temos a classe MessageFormatter. Assim como a classe MessageInspector, o formatador da mensagem também pode ser interceptado tanto do lado do serviço quanto do lado do cliente e, justamente por isso, implementa as duas interfaces IDispatchMessageFormatter e IClientMessageFormatter. Do lado do serviço, temos os métodos DeserializeRequest e SerializeReply, sendo o primeiro deles disparado no momento da deserialização da requisição e, o segundo, no momento da serialização da resposta para o cliente. Já a interface IClientMessageFormatter disponibiliza os métodos DeserializeReply e SerializeRequest, invocando no momento da deserialização da resposta e na serialização da requisição, respectivamente. Com a implementação dessas interfaces podemos intervir em como os parâmetros e/ou resposta será serializado ou deserialização na mensagem.

Modificando o Runtime

Depois que analisamos todos os tipos que temos para customizar um ponto de extensão dentro do WCF, precisamos saber como acoplá-los na execução do serviço. As classes que implementamos acima não trabalham por si só; elas precisam ser incluídas em algum ponto dentro da execução, seja do lado do cliente (proxy) ou do lado serviço (dispatcher).

Uma das formas que temos para efetuar este acoplamento é utilizando os behaviors customizados. Atualmente temos quatro escopos diferentes de behaviors: operation behavior, contract behavior, endpoint behavior e service behavior. O primeiro deles afetará apenas a execução de uma operação específica. Os contract behaviors nos permite aplicar algo customizado para um determinado contrato. Já os endpoint behaviors são usados exclusivamente para um endpoint. E, finalmente, os service behaviors possibilitam a execução de algo para o serviço como um todo, independentemente de quantos endpoints houverem. O WCF já traz vários behaviors criados prontos para serem utilizados e, possibilita também criarmos nossos próprios behaviors, apenas implementando as interfaces IOperationBehavior, IContractBehavior, IEndpointBehavior ou IServiceBehavior.

Cada uma dessas Interfaces está dentro do namespace System.ServiceModel.Description, e fornecem métodos com nomes semelhantes, mas com a assinatura diferenciada, disponibilizando os objetos pertinentes ao contexto atual, como por exemplo as interfaces IOperationBehavior, IContractBehavior e IEndpointBehavior possuem quatro métodos: AddBindingParameters, ApplyClientBehavior, ApplyDispatchBehavior e Validate. Todos eles não retornam nenhuma informação (void) e, apesar de ter o mesmo nome e tipo de retorno em todas as interfaces, os parâmetros dos métodos nas interfaces são diferentes, justamente para trazer informações referentes aquele behavior.

Para tornar mais claro, se analisarmos o método ApplyClientBehavior da interface IOperationBehavior, ele traz em seu parâmetro um objeto do tipo ClientOperation, enquanto neste mesmo método da interface IEndpointBehavior, ele fornece o objeto ClientRuntime. E já que estamos falando de métodos, ApplyClientBehavior e ApplyDispatchBehavior são utilizados para extrairmos e/ou adicionarmos alguma informação nesses dois importantes objetos que fazem parte do runtime do WCF. Já o método AddBindingParameters disponibiliza uma coleção contendo os parâmetros do binding que estão vinculados a execução atual e, finalmente, o método Validate que nos permite efetuar alguma validação em cima do behavior (entenda isso como validar o contrato, endpoint, operação ou o serviço como um todo).

Observação: A única diferença da interface IServiceBehavior é que ela não possui o método ApplyClientBehavior. Isso se deve ao fato de que essa interface somente se aplica a customização do serviço.

Seguindo a mesma linha de explicação que utilizei acima para descrever cada uma das implementações de extensibilidades, vou também poupar espaço aqui ao invés de colocar todas as implementações de behaviors customizados, que precisamos criar para injetar o código criado acima. A imagem a seguir também ilustra os behaviors que fazem parte da solução, e será interessante abrir o código fonte vinculado a este artigo para acompanhar as explicações.

Figura 6 – Estrutura dos behaviros customizados.

Inicialmente vamos falar sobre a classe ParameterInspectorBehaviorAttribute. Ela implementa a interface IOperationBehavior, customizando os métodos ApplyClientBehavior e ApplyDispatchBehavior. O primeiro deles fornece um parâmetro do tipo ClientOperation que, via propriedade ParameterInspectors, podemos adicionar uma instância de uma classe que implemente a interface IParameterInspector. O método ApplyDispatchBehavior trabalha de forma semelhante, mas como será aplicado ao serviço, então como parâmetro recebe uma instância da classe DispatchOperation.

A classe OperationSelectorBehaviorAttribute implementa a interface IEndpointBehavior, customizando também os métodos ApplyClientBehavior e ApplyDispatchBehavior. Ambos métodos expõe direta ao indiretamente os objetos ClientRuntime e DispatchRuntime respectivamente, e ambos oferecem uma propriedade chamada OperationSelector, onde podemos customizar como será efetuada a seleção da operação. Nos dois casos, estaremos definindo nesta propriedade a instância da classe OperationSelector, que criamos previamente.

Na sequência, temos a classe OperationInvokerBehaviorAttribute que implementa a interface IOperationBehavior, mas customizará apenas o lado do serviço. O método ApplyDispatchBehavior fornece uma instância da classe DispatchOperation que, por sua vez, disponibiliza uma propriedade chamada Invoker, onde definiremos uma classe que implemente a interface IOperationInvoker, que no nosso caso é a classe OperationInvoker, para customizarmos como a operação será invocada. E, finalmente, temos as classes MessageInspectorBehaviorAttribute e MessageFormatterBehaviorAttribute. A primeira responsável por inserir uma instância da classe MessageInspector na execução do serviço e do cliente, implementando as interfaces IEndpointBehavior e IServiceBehavior. E, para encerrar, a classe MessageFormatterBehaviorAttribute implementa a interface IEndpointBehavior, para definir uma instância da classe MessageFormatter para as operações tanto do lado do serviço (DispatchOperation) quanto do lado do cliente (ClientOperation), através da propriedade Formatter.

Todas essas classes implementam uma das interfaces de behaviors customizados, mas ainda não trabalham por si só. Ainda precisamos acoplar as instâncias dessas classes recém criados em algum ponto da execução. Se você notar, todas as classes herdam da classe Attribute. Isso se deve ao fato de que, podemos utilizar a programação declarativa, para adicionarmos essas classes à execução do WCF. Durante a execução, o WCF utiliza Reflection para extrair o behavior configurado via atributo e o adicioná-lo no local correto. Desta forma, a classe que representa o serviço já está decorada com alguns atributos criados, como podemos visualizar abaixo:

[MessageInspectorBehavior]
public class ServicoDeUsuarios : IUsuarios
{
    [OperationInvokerBehavior]
    [ParameterInspectorBehavior]
    public bool Adicionar(Usuario usuario)
    {
        Console.WriteLine("Usuario adicionado: {0}", usuario.Nome);
        return true;
    }

    [OperationInvokerBehavior]
    [ParameterInspectorBehavior]
    public Usuario[] RecuperarUsuarios()
    {
        return new Usuario[]
        {
            new Usuario(){ Nome = "Israel" },
            new Usuario(){ Nome = "Claudia" },
            new Usuario(){ Nome = "Juliano" }
        };
    }
}

As configurações que foram realizadas acima apenas refletem em mudanças do lado do serviço. Os mesmos atributos podem ser utilizados do lado do cliente e, justamente por isso, que eles foram criados em um Assembly isolado. Como sabemos, ao referenciar o serviço em uma aplicação cliente, o proxy é gerado e, com isso, uma interface contendo a mesma estrutura de métodos do serviço também é criada. Sendo assim, é perfeitamente possível aplicarmos os atributos na interface do proxy, quando desejado, interceptando a execução, mas agora do lado do cliente.

Já aqueles behaviors que implementam a interface IEndpointBehavior, devem ser adicionados na coleção de behaviors de um endpoint do lado do serviço ou do cliente. Do lado do serviço, ao criar um endpoint através do método AddServiceEndpoint da classe ServiceHost, ele retorna uma instância da classe ServiceEndpoint que, por sua vez, fornece uma propriedade chamada Behaviors que aceita instâncias da classe que implementam a interface IEndpointBehavior e, do lado do cliente, o proxy (ClientBase<TChannel>) trabalha da mesma forma, expondo uma propriedade chamada Endpoint.

Além das formas que vimos acima para adicionar um behavior à execução, podemos ainda definir esses behaviors através do arquivo de configuração, para assim ter uma maior flexibilidade. O WCF disponibiliza uma seção chamada behaviors para endpoints e outro para o serviço. Qualquer elemento elencado em uma destas coleções será automaticamente adicionado ao serviço/endpoint. Para suportar esta técnica, ainda é necessário a criação de uma classe adicional que representará o elemento dentro do arquivo de configuração. Esta classe deve herdar da classe abstrata BehaviorExtensionElement e sobrescrever os membros BehaviorType e CreateBehavior. O exemplo abaixo ilustra a criação de um desses elementos:

using System.ServiceModel.Configuration;

public class OperationSelectorElement : BehaviorExtensionElement
{
    public override Type BehaviorType
    {
        get
        {
            return typeof(OperationSelectorBehaviorAttribute);
        }
    }

    protected override object CreateBehavior()
    {
        return new OperationSelectorBehaviorAttribute();
    }
}

Basicamente, o método CreateBehavior retorna uma instância da classe que implementa uma das interfaces IServiceBehavior ou IEndpointBehavior. Depois desta classe criada, podemos inserir o behavior via arquivo de configuração. No código de exemplo vinculado ao artigo, o código cliente insere o behavior OperationSelector utilizando o código declarativo. Note que primeiramente é necessário registrar o behavior através da seção behaviorExtensions para em seguida utilizá-lo. O exemplo abaixo ilustra o registro e sua utilização:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <client>
      <endpoint
          address="net.tcp://localhost:3838/srv" binding="netTcpBinding"
          bindingConfiguration="NetTcpBinding_IUsuarios"
          contract="ServicoDeUsuarios.IUsuarios"
          name="NetTcpBinding_IUsuarios"
          behaviorConfiguration="edpConfig">
      </endpoint>
    </client>
    <extensions>
      <behaviorExtensions>
        <add 
          name="operationSelector" 
          type="Service.Extensions.Configuration.OperationSelectorElement, Service"/>
      </behaviorExtensions>
    </extensions>
    <behaviors>
      <endpointBehaviors>
        <behavior name="edpConfig">
          <operationSelector />
        </behavior>
      </endpointBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Compartilhando Estado

As opções que vimos acima no permite customizar algum comportamento da execução do WCF. Além destas técnicas, ainda temos um mecanismo para armazenar o estado de um objeto em diferentes contextos da execução, mantendo-o durante o tempo necessário. Você poderá criar essas extensões em diferentes contextos e, para isso, é necessário criar uma classe que implemente a interface IExtension<T>.

Essa interface fornece um parâmetro chamado T que, por sua vez, aceita qualquer tipo que implemente a interface IExtensibleObject<T>. Dentro do próprio WCF, temos as seguintes classes que implementam essa interface: ServiceHost, InstanceContext e OperationContext. Objetos inseridos dentro do contexto da classe ServiceHost, serão mantidos entre toda a vida do host; já os objetos inseridos na classe InstanceContext terão o contexto determinado pelo tempo de vida da instância da classe que representa o serviço e, finalmente, objetos inseridos na classe OperationContext, terão o seu tempo de vida mais reduzido, ficando ativo somente durante a execução de uma operação.

A interface IExtension<T> é bem simples, pois fornece apenas dois métodos chamados Attach e Detach. O primeiro deles é invocado quando a instância da classe que implementa esta interface for adicionada à coleção de extensões, através de uma propriedade chamada Extensions. Essa propriedade é fornecida por todas as classes que implementam a interface IExtensibleObject<T>. Já o segundo método, Detach, é disparado quando a instância desta classe for removida da coleção Extensions.

using System;
using System.ServiceModel;

public class ServiceHostBaseExtension : IExtension<ServiceHostBase>
{
    public void Attach(ServiceHostBase owner)
    {
        //...
    }

    public void Detach(ServiceHostBase owner)
    {
        //...
    }
}

Note que implementamos a interface IExtension<T> definindo como tipo T a classe ServiceHostBase e, devido aos parâmetros genéricos, a instância da classe ServiceHostBase ao qual essa extensão for adicionada, é passada como parâmetro para os métodos Attach e Detach. Como definimos o tipo ServiceHostBase, o código abaixo ilustra como adicionar a instância dessa classe à propriedade Extensions do ServiceHost:

using (ServiceHost host = new ServiceHost(typeof(ServicoDeUsuarios), 
    new Uri[] { new Uri("net.tcp://localhost:3838") }))
{
    //outras configurações

    host.Extensions.Add(new ServiceHostBaseExtension());

    host.Open();

    Console.ReadLine();
}

Conclusão: Vimos ao longo deste artigo grande parte das possibilidades que temos dentro do WCF para extendê-lo. Com os exemplos mostrados aqui, podemos customizar grande parte da execução de um serviço, tendo vários pontos de interceptação tanto do lado do serviço quanto do lado do cliente.

InternalsExtensibilidade.zip (289.24 kb)

Serviços CRUD

Muitas pessoas utilizam ou já utilizaram o WCF (ou até mesmo os antigos ASP.NET Web Services (ASMX)), para servir como um wrapper de uma base de dados. Basicamente era criado um serviço para cada entidade desta base, onde cada um deles apenas define em sua interface as operações de CRUD, que nada mais são do que as operações básicas com uma determinada tabela relacional.

Se você ainda precisa criar algum tipo que serviço que exponha essas funcionalidades, então acredito que seria uma boa alternativa considerar o uso do ADO.NET Data Services. Este framework é construído em cima do próprio WCF, fornecendo a possibilidade de efetuar as operações CRUD em cima de contexto de dados do Entity Framework ou qualquer outra fonte de dados que implemente a interface IQueryable. Todas as funcionalidades são baseadas no padrão REST, que utiliza URIs predefinidas em conjunto com os verbos HTTP, para executar cada uma dessas operações. Já a serialização do resultado pode ser emitida em ATOM (Xml) ou até mesmo JSON, permitindo assim, que qualquer cliente HTTP (como um navegador) consuma o serviço.

Um detalhe importante é que a Microsoft incluiu no .NET Client Library, Silverlight e no AJAX tudo o que é necessário para efetuar a comunicação com serviços baseados no ADO.NET Data Services.

É importante dizer que o ADO.NET Data Services não é ideal para todos os cenários. Há muitas ocasiões onde a customização pode ser muito grande, e utilizando-o pode tornar o processo muito mais trabalhoso do que produtivo e, sendo assim, será mais viável utilizar o WCF, podendo inclusive, expor as funcionalidades do serviço utilizando o padrão REST, se assim desejar. Agora, se tudo o que precisa são as simples operações de CRUD, então criar serviços baseados no ADO.NET Data Services será uma opção boa e bastante produtiva.

Validando a Identidade do Serviço

O WCF fornece várias possibilidades para autenticar o cliente. Isso permitirá ao serviço somente possibilitar o acesso ao mesmo, se a autenticação do cliente for realizada com sucesso, independente do tipo de credencial e de como isso é avaliado. É muito comum quando falamos de autenticação, em pensar que somente isso acontecerá do lado do serviço.

Da mesma forma que o serviço precisa validar o cliente para conceder o acesso, o cliente também precisa validar o serviço, para saber se ele está enviando a mensagem para o local correto. Felizmente o WCF possibilita a autenticação mútua, ou seja, antes de efetivamente enviar a mensagem para o serviço, o cliente primeiramente fará algumas validações para determinar se o serviço para qual ele enviará a mensagem, é realmente quem ele diz ser.

Depois que o cliente inicia a comunicação com um endpoint e depois que o serviço o autentica, ele irá comparar a identidade do endpoint remoto com a identidade fornecida para o cliente durante a referência do mesmo, através do documento WSDL. Se os valores forem iguais, então o cliente assume que é o serviço correto. Isso irá proteger o cliente de possíveis ataques de phishing, evitando que o mesmo seja redirecionado para endpoints maliciosos.

Essa validação ocorre de forma transparente e dependerá de qual tipo de identidade é fornecida pelo serviço. Atualmente temos cinco possibilidades: Domain Name System (DNS), Certificate, Certificate Reference, RSA, UPN (User Principal Name) e SPN (Service Principal Name). O uso de cada um deles dependerá do cenário e dos requerimentos de segurança exigidos. Para saber mais detalhes sobre cada um deles, consulte este artigo.

Se você quiser interceptar esse processo, você poderá criar um “autenticador” customizado. Isso nos permitirá customizar como essa validação deverá ser realizada, ficando sob nossa responsabilidade determinar se o serviço para qual o cliente está tentando enviar a mensagem é válido ou não.

Para que isso seja possível, é necessário criarmos uma classe que herde da classe abstrata IdentityVerifier (namespace System.ServiceModel.Security) e implementar os métodos TryGetIdentity e CheckAccess. O primeiro deles, recebe um parâmetro de output do tipo EnpointIdentity que será populado, com um objeto que representa a identidade remota do serviço, retornando um valor boleano indicando se a identidade foi ou não criada. O segundo método, CheckAccess, é onde devemos efetivamente validar se a identidade do serviço é realmente válida. Esse método também retorna um valor boleano, só que neste caso, indicando se a identidade é válida ou não, de acordo com a regra que vamos customizar. O código abaixo ilustra a implementação desta classe:

public class ValidadorDeIdentidadeDoServico : IdentityVerifier
{
    public override bool CheckAccess(EndpointIdentity identity, AuthorizationContext authContext)
    {
        foreach (var set in authContext.ClaimSets)
        {
            foreach (var claim in set)
            {
                Console.WriteLine(claim.ClaimType);
                Console.WriteLine(“t{0}”, claim.Resource);
            }
        }

        Console.WriteLine();
        return true;
    }

    public override bool TryGetIdentity(EndpointAddress reference,
        out EndpointIdentity identity)
    {
        return IdentityVerifier.CreateDefault().TryGetIdentity(reference, out identity);
    }
}

Depois dessa classe criada, precisamos acoplá-la na execução do proxy. Mas para isso, iremos precisar de um binding customizado. Através do método abaixo vamos efetuar essa configuração. Note que ele atribui a instância da classe ValidadorDeIdentidadeDoServico na propriedade IdentityVerifier, que será responsável pela validação do serviço.

public static Binding ConfigurarBinding()
{
    WSHttpBinding binding = new WSHttpBinding(SecurityMode.Message);
    binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
    binding.Security.Message.EstablishSecurityContext = true;

    BindingElementCollection elements = binding.CreateBindingElements();
    SymmetricSecurityBindingElement ssbe =
        (SymmetricSecurityBindingElement)elements.Find<SecurityBindingElement>();

    ssbe.LocalClientSettings.IdentityVerifier = new ValidadorDeIdentidadeDoServico();
    return new CustomBinding(elements);
}

Para finalizar, temos que configurar o proxy para, ao invés de utilizar a configuração padrão do arquivo de configuração, usar o binding customizado, onde definimos a classe que será responsável por efetuar a validação do serviço. O código abaixo ilustra como fazemos a vinculação do binding ao proxy:

using (ContratoClient p =
    new ContratoClient(ConfigurarBinding(),
    new EndpointAddress(“http://localhost:2334/srv&#8221;)))
{
    //…
}

WSDL com nome da máquina

Quando publicamos um serviço WCF em um servidor, mais precisamente, dentro do IIS, é comum aparecer o nome da máquina no endereço de acesso ao documento que descreve o serviço (WSDL). Como se isso não bastasse, dentro do WSDL ainda existem diversos outros links, que também utilizam o nome da máquina para compor o endereço. Mas o correto seria aparecer o nome do domínio ou, até mesmo, o IP do servidor onde o serviço está hospedado.

Isso ocorre porque o WCF utiliza as configurações do IIS (binding information) para determinar o base address do serviço. Para resolver isso, podemos atualizar as configurações do IIS. Caso esteja utilizando o IIS 6.0, então pode adicionar um host header customizado nas configurações do Web Site, ou ainda, através do seguinte comando:

C:WINDOWSsystem32>cscript.exe %systemdrive%inetpubadminscriptsadsutil.vbs set w3svc/<ID do Web Site>/ServerBindings “:80:www.dominio.com.br”

Se estiver utilizando o IIS 7.0, pode recorrer a configuração do arquivo applicationHost.config, assim como é mostrado abaixo:

<bindings>
  <binding protocol=”http” bindingInformation=”*:80:www.dominio.com.br” />
</bindings>

E, finalmente, se já estiver utilizando o .NET Framework 3.5 SP 1, então pode utilizar o elemento baseAddressPrefixFilters, assim como é mostrado neste post.

Configurando o limite de conexões pendentes

Quando expomos um serviço através do binding NetTcpBinding, as requisições que chegam até ele, são processadas e, finalmente o retorno é enviado para o respectivo cliente. As mensagens que chegam são processadas no formato FIFO (first-in, first out), ou seja, elas aguardam o processamento em uma fila e, sob demanda, são encaminhadas para o processamento.

Se a velocidade do processamento da mensagem é mais lenta que a velocidade de envio das mensagens, essa fila pode aumentar. Por padrão o binding NetTcpBinding limita, através da propriedade ListenBacklog, o número de requisições pendentes que podem ser enfileiradas. O valor padrão é 10 e, dependendo da quantidade de requisições que chegam para o serviço, esse limite pode exceder e, a seguinte exceção será disparada:

System.ServiceModel.EndpointNotFoundException: Could not connect to net.tcp://localhost:8879/srv. The connection attempt lasted for a time span of 00:00:02.1404880. TCP error code 10061: No connection could be made because the target machine actively refused it 127.0.0.1:8879.  —> System.Net.Sockets.SocketException:No connection could be made because the target machine actively refused it 127.0.0.1:8879

A alternativa aqui é aumentar esse número, definindo um valor próximo a quantidade das conexões simultaneas que eventualmente podem chegar para o serviço. O exemplo abaixo ilustra a configuração desta propriedade no binding NetTcpBinding:

host.AddServiceEndpoint(typeof(IContrato), new NetTcpBinding() { ListenBacklog = 70 }, “srv”);

Serialização de Propriedades Automáticas

Quando queremos expor uma propriedade, na maioria das vezes existe um campo privado que serve como “repositório” para a informação que é definida ou lida. A propriedade tem o papel de interceptar a leitura e escrita à este campo privado através de blocos get/set.

Para reduzir o trabalho, a partir da versão 3.0 do C#, é disponibilizado uma funcionalidade chamada de Automatic Properties que, por sua vez, nos permite omitir a criação deste campo privado. Na realidade, essa funcionalidade nada mais é do que “açúcar sintático”, já que no momento da compilação, o C# irá injetar o campo privado para armazenar a informação que a propriedade expõe. Como exemplo, imagine que voce cria uma classe com a seguinte definição:

public class Usuario
{
    public string Nome { get; set; }
}

No momento da compilação, ela será definida como:

public class Usuario
{
    [CompilerGenerated]
    private string <Nome>k__BackingField;

    public string Nome
    {
        [CompilerGenerated]
        get
        {
            return this.<Nome>k__BackingField;
        }
        [CompilerGenerated]
        set
        {
            this.<Nome>k__BackingField = value;
        }
    }
}

Já vi alguns casos em que esses objetos que fazem uso desta técnica, são marcados com o atributo SerializableAttribute e, como o próprio atributo indica, permitir a serialização do mesmo. Quando um objeto com essas características são expostos através de serviços WCF (talvez quando voce já possuir uma API com essas classes definidas), precisamos de alguns cuidados especiais.

O serializador padrão do WCF é o DataContractSerializer. Esse serializador trabalha em conjunto com os atributos DataContractAttribute e DataMemberAttribute, serializando as propriedades ou campos públicos que são decorados com um desses dois atributos. Esse serializador também suporta classes decoradas com o atributo SerializableAttribute mas, como esse atributo foi desenhado para persistir e reconstituir na íntegra a representação do objeto, todos os campos (privados ou públicos) são serializados. As propriedades aqui são ignoradas.

Ao submeter um objeto com as características que vimos acima para o serializador padrão do WCF, ele persistirá os campos privados, que no nosso caso é o <Nome>k__BackingField, e que na maioria dos casos, não é isso que desejamos. Como exemplo, podemos utilizar o seguinte código para analisar o resultado deste processo:

new DataContractSerializer(typeof(Usuario))
    .WriteObject(
        File.Create(“Usuario.xml”),
        new Usuario() { Nome = “Israel” });

Abaixo temos conteúdo Xml correspondente:

<Usuario xmlns=”…” xmlns:i=”…”>
  <_x003C_Nome_x003E_k__BackingField>Israel</_x003C_Nome_x003E_k__BackingField>
</Usuario>

Caso voce não possa alterar os atributos que já foram definidos para a classe, uma possibilidade que temos é utilizar o serializador XmlSerializer. Ao contrário do DataContractSerializer, esta classe irá serializar somente as propriedades ou campos públicos. Assim como o exemplo acima, para efetuarmos este teste, podemos utilizar o seguinte código:

new XmlSerializer(typeof(Usuario))
    .Serialize(
        File.Create(“Usuario.xml”),
        new Usuario() { Nome = “Israel” });

E, finalmente, o resultado em Xml:

<?xml version=”1.0″?>
<Usuario xmlns:xsi=”…” xmlns:xsd=”…”>
  <Nome>Israel</Nome>
</Usuario>

O XmlSerializer é utilizado também pelo ASP.NET Web Services (ASMX). Por mais que esta técnica funcione, antes de alterar o serializador padrão do WCF, é importante que voce analise as vantagens e desvantagens da sua utilização e, para isso, poderá recorrer à própria documentação.

ASP.NET Development Server parou de funcionar

Com a instalação de uma atualização que a Microsoft lliberou, o arquivo hosts (C:WindowsSystem32driversetc) pode ter sido alterado, removendo a entrada que tem o apontamento do IP 127.0.0.1 para localhost, fazendo com que os projetos ASP.NET que usam o servidor interno do Visual Studio .NET para hospedar as aplicações deixem de funcionar. Com isso, ao rodar essas aplicações, voce receberá a seguinte mensagem: “Internet Explorer cannot display the webpage”.

Como já era de se esperar, basta readicionar esta linha no arquivo para que tudo volte ao normal, assim como é mostrado abaixo:

127.0.0.1       localhost

Compartilhando portas através do protocolo TCP

Ao criar um serviço WCF, podemos disponibilizá-lo através do protocolo TCP (net.tcp), utilizando uma porta específica. Uma vez que voce publica esse serviço, nenhum outro serviço naquela mesma máquina poderá utilizar aquela porta ao mesmo tempo, e se fizer isso, uma exceção do tipo AddressAlreadyInUseException será disparada exibindo a seguinte mensagem: There is already a listener on IP endpoint [MachineName]:[Port].  Make sure that you are not trying to use this endpoint multiple times in your application and that there are no other applications listening on this endpoint.

Podemos não perceber mas o Windows permite o compartilhamento de uma mesma porta TCP para expor múltiplas aplicações. Um exemplo disso é o HTTP, que permite múltiplas aplicações estarem acessíveis através da porta 80 do protocolo TCP. Isso é possível graças à um listener (HTTP.SYS) que o IIS possue e que, baseando-se no conteúdo da mensagem, encaminha a requisição para a aplicação correspondente processá-la.

Ao instalar o .NET Framework, um serviço chamado Net.Tcp Port Sharing Service é instalado no Windows, dando a possibilidade de múltiplos serviços WCF compartilharem a mesma porta TCP, assim como o HTTP. Esse serviço (hospedado através do processo SMSvcHost.exe (C:WindowsMicrosoft.NETFrameworkv3.0Windows Communication Foundation)) tem a finalidade de inspecionar a mensagem, extrair o endereço de destino, e baseando-se nele, encaminhar a requisição para a aplicação que a processará.

Além deste serviço funcionando, ainda é necessário uma pequena configuração no código que expõe serviço WCF. O binding NetTcpBinding possui uma propriedade booleana chamada PortSharingEnabled que, por padrão, é definida como False. Voce deverá definí-la como True para todos os serviços que desejarem compartilhar uma porta TCP específica. Ao fazer isso, o WCF se encarrega de iniciar automaticamente o serviço Net.Tcp Port Sharing Service, mas voce pode fazer isso manualmente, ou ainda alterar a configuração do serviço para ele ser inciado automaticamente quando o sistema operacional entrar no ar. O trecho de código abaixo ilustra como podemos proceder para configurar o endpoint com o binding e habilitando o compartilhamento da porta TCP:

host.AddServiceEndpoint(
    typeof(IContrato),
    new NetTcpBinding() { PortSharingEnabled = true }, 
    new Uri(“net.tcp://localhost:7373/NomeDoServico/Cadastro”));

Como os serviços irão compartilhar a mesma porta, a distinção da aplicação é determinada pela URI como um todo, ou seja, o que vem depois da porta na URI do serviço precisa ser diferente entre os serviços, caso contrário voce terá o mesmo problema que antes.

Com esta técnica, vamos permitir múltiplas aplicações coexistirem fisicamente na mesma máquina, mas em processos separados, compartilhando a mesma infraestrutura de rede, melhorando a segurança, já que podemos diminiur a superfície de ataque não exigindo a abertura de portas no firewall de forma aleatória.