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.

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.

IgnoreDataMemberAttribute

A partir do .NET Framework 3.5 SP1 temos a possibilidade de suportar a serialização de tipos complexos em serviços WCF, mesmo que eles não estejam decorados com o atributo DataContractAttribute, DataMemberAttribute ou SerializableAttribute, permitindo o suporte à objetos POCO.

Quando trabalhamos nesta linha, não precisamos nos preocupar com os atributos já que, por padrão, todas as propriedades públicas serão serializadas. Já que o comportamento padrão faz com que todas as propriedades serão serializadas, como podemos determinar que uma delas não deve ser disponibilizada para os consumidores do serviço? Neste caso, temos que explicitamente dizer ao runtime do WCF para não serializar tal propriedade e, para isso, recorremos à utilização do atributo IgnoreDataMemberAttribute, contida no namespace System.Runtime.Serialization, aplicando-o nas propriedades que não queremos que esteja disponível, assim como é mostrado abaixo:

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

    [IgnoreDataMember]
    public string Password { get; set; }
}

Expondo serviços WCF através do .NET Service Bus

Depois da conta devidamente criada dentro do Windows Azure, podemos facilmente fazer uso das funcionalidades que ele nos fornece. Dentre essas funcionalidades temos o .NET Services e, dentro deste, um repositório de serviços chamado .NET Service Bus. Utilizando o WCF, podemos conectar duas aplicações que estão localizadas em diferentes locais, ultrapassando os limites de uma organização, utilizando a “ponte” que é o Service Bus.

Em termos de estrutura e criação de um serviço, nada muda em relação ao que já conhecemos. As pequenas mudanças são na configuração do mesmo, a começar pelo binding, já que devemos utilizar um dos relay bindings. Além disso, há um novo behavior chamado TransportClientEndpointBehavior, que nos permite configurar as credenciais de acesso, assim como é ilustrado na configuração do host logo abaixo:

<?xml version=”1.0″ encoding=”utf-8″ ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name=”Host.Servico”>
        <endpoint
          address=”sb://servicebus.windows.net/services/[solucao]/[servico]”
          behaviorConfiguration=”bh”
          binding=”netTcpRelayBinding”
          contract=”Host.IContrato” />
      </service>
    </services>
    <behaviors>
      <endpointBehaviors>
        <behavior name=”bh”>
          <transportClientEndpointBehavior
            credentialType=”UserNamePassword”>
            <clientCredentials>
              <userNamePassword
                 userName=”[username]” password=”[password]” />
            </clientCredentials>
          </transportClientEndpointBehavior>
        </behavior>
      </endpointBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Outro ponto importante que devemos notar é o endereço. Neste CTP, o .NET Service Bus apenas suporta dois schemes: “sb” e “http”. O “http” para protocolo HTTP e o “sb” para todos os outros. De acordo com o modelo de nomenclatura, “servicebus.windows.net” é a raiz, e com isso os endereços devem seguir o seguinte formato: “[scheme]://servicebus.windows.net/services/[solucao]/[servico]/[nome]”. Em futuras versões ele suportará diferentes formatos na URI.

As tipos que utilizamos para expor/consumir serviços WCF em cima do .NET Service Bus, estão contidos no Assembly Microsoft.ServiceBus.dll  que, por sua vez, é instalado com o SDK do Microsoft .NET Services (CTP). Ao rodar o host, o serviço está exposto e pode ser consumido pelos clientes. Ao iniciar o cliente que consome este serviço, as mensagens passam agora pelo .NET Service Bus até chegar efetivamente ao serviço.

Utilizando Datasets em serviços

Muitas pessoas utilizam Datasets e, consequentemente, DataTables para representar sua estrutura de dados. Com isso, pode haver a necessidade de expor esse tipo de objeto através de serviços (WCF ou ASMX). Como o Dataset pode ser serializado em formato Xml, então podemos fazer uso dele nestes serviços.

O problema que há neste caso é que isso é uma má prática do ponto de vista de interoperabilidade e, em alguns casos, de performance. A estrutura Xml que representa o Dataset é complexa e difícil de ser representada em outras plataformas que não seja o próprio .NET. Além disso, devido também a complexidade dele, a tamanho do envelope SOAP fica muito maior em relação à arrays de objetos (DTOs). E, para finalizar, se voce utiliza o Dataset como estrutura dos dados na aplicação, voce pode indiretamente expor a sua estrutura interna para o mundo.

Sempre que possível opte pela utilização de classes que são desenhadas (exclusivamente ou não) para serem trocadas entre o serviço e os clientes. Já quando precisar retorna uma lista, opte pelos tradicionais arrays. Isso garantirá um payload muito mais leve e, além disso, irá garantir a interoperabilidade com as mais diversas plataformas.

Utilização de entidades LINQ To SQL em serviços

Estive lendo um artigo na MSDN Magazine chamado Flexible Data Access With LINQ To SQL And The Entity Framework, escrito pelo Anthony Sneed. O artigo aborda como expor entidades criadas pelos ORMs da Microsoft (LINQ To SQL e Entity Framework) através de serviços WCF.

O autor criou DTOs (que são baseados em POCO) semelhantes as entidades criadas pelo designer do ORM, fazendo um trabalho árduo que é o mapeamento entre os DTOs e as entidades geradas. Como o LINQ To SQL tem um suporte legal para mapeamento entre objetos POCOs e o banco de dados (via arquivos Xml), o Anthony mostra neste post como podemos fazer uso dos templates T4, criando classes em formato POCO ao invés de utilizar as próprias entidades geradas pelo LINQ To SQL.

KnownTypes dinâmicos

Para lidar com classes e suas derivações em serviços, o WCF disponibiliza um recurso chamado Known Types. Com ele, é possível ter um método que defina como retorno um tipo base e, em seu interior, criar e retornar tipos derivados.

Para expor os tipos derivados a partir deste recurso, como se pode ver no artigo mencionado acima, voce deverá decorar a Interface ou os métodos que ela possuir  (se quiser ser mais restritivo) com o atributo ServiceKnownTypeAttribute. No artigo podemos ver que os tipos derivados são declarados estaticamente. Caso voce queira tornar a configuração dos known types do serviço de forma dinamica, voce poderá criar um método que retorne os tipos que serão suportados pelo serviço, como é mostrado abaixo:

private static class ServiceContractHelper
{
    public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
    {
        List<Type> knownTypes = new List<Type>();
        knownTypes.Add(typeof(Fisica));
        knownTypes.Add(typeof(Juridica));

        return knownTypes;
    }
}

Para que essa técnica funcione, é necessário que voce defina a assinatura do método como é mostrado acima, podendo variar o nome. Em seguida precisamos vincular esta classe ao contrato e, para isso, utilizamos um dos overloads do construtor do atributo ServiceKnownTypeAttribute que, receberá uma string contendo o nome do método que fornecerá os tipos e, um Type representando o tipo da classe que fornece o método.

[ServiceContract]
[ServiceKnownType(“GetKnownTypes”, typeof(ServiceContractHelper))]
public interface IGerenciadorDeContatos
{
    //métodos
}

Criando certificados para teste

Algumas funcionalidades de segurança do WCF exigem que se utilize certificados para proteger a mensagem. Eu tenho escrito sobre algumas dessas funcionalidades aqui e, no download do código fonte, o certificado não está junto com o projeto. Com isso, ao rodar a aplicação, voce receberá uma exceção dizendo que não foi possível localizar este certificado. Isso aconteceu recentemente com o artigo sobre customização de autenticação e autorização e que o leitor Marco Tulio apontou aqui.

Neste artigo específico, para que voce consiga rodá-lo, o primeiro passo é criar um certificado de testes a partir do utilitário makecert.exe que, por sua vez, vem juntamente com o SDK do .NET Framework. Para utilizá-lo, basta definir algumas configurações necessárias para a geração do certificado, algo como é mostrado abaixo:

C:>makecert -sr LocalMachine -ss TrustedPeople -sky exchange -pe -a sha1 -n “CN=localhost” C:localhost.cer

Observação: Para entender cada uma dessas opções que o utilitário fornece, consulte este artigo.

Depois do certificado criado, então nos resta configurá-lo para que o serviço possa fazer uso dele. Ao abrir o arquivo de configuração do host, efetue a mudança do certificado, apontando para este que foi recém criado. O código ficará semelhante ao qual é mostrado abaixo:

<serviceCredentials>
  <serviceCertificate
    findValue=”localhost”
    storeLocation=”LocalMachine”
    storeName=”TrustedPeople”
    x509FindType=”FindBySubjectName” />
    <!– Outras Configurações –>
</serviceCredentials>

Depois disso, execute o host separadamente e atualize a referencia no projeto cliente para que a chave pública seja alterada.

Para finalizar, ainda falando do artigo que explora a customização da autenticação e autorização no WCF, é necessário alterar o path dos arquivos XML que estão dentro da classe/arquivo XmlSecurityHelper.

WCF Performance Counters II

Algum tempo atrás eu falei comentei aqui sobre vários performance counters que foram adicionados ao runtime do WCF, disponibilizando diversas informações a respeito da execução dos serviços, tais como: número de chamadas, transações, segurança, etc.

Como sabemos, podemos fazer uso do IIS como host de serviços WCF e, com isso, utilizar alguns performance counters exclusivos de aplicações Web, instalados juntamente com o ASP.NET. Esses performance counters mensuram, entre várias informações importantes, a quantidade de requisições para uma determinada aplicação ou para um worker process. Utilizar esses performance counters para mensurar requisições para serviços WCF não pode ser muito confiável.

Há funcionalidades que quando habilitadas nos serviços WCF, como é o caso das reliable messages, fazem com que várias outras requisições sejam efetuadas nos bastidores, garantindo com que este recurso funcione e, como já era de se esperar, representará mais do que uma “requisição ASP.NET”, não refletindo exatamente o que está ocorrendo naquele momento. Quando precisar mensurar alguma estatística de serviços WCF, independentemente de onde eles estejam sendo hospedados (Windows Service, IIS, etc.), sempre opte por utilizar os performance counters exclusivos, agrupando as informações através de contadores específicos para cada funcionalidade, como é caso dos contadores que contabilizam as reliable messages.

Liberando Streams

Há algum tempo escrevi um artigo sobre transferencia e codificação de dados no WCF. Quando optamos por utilizar o modelo streaming, há um cuidado extra que devemos tomar para efetuar o fechamento dele. Neste cenário sempre há o desafio de saber quando e quem fecha o stream e que foram abordadas no artigo mecionado acima.

Quando alguém envia um stream para o outro lado, este deverá permanecer aberto até que a transferencia seja concluída. Não temos problemas quando o stream é passado diretamente com parametro ou como retorno de uma operação. O problema começa a acontecer quando voce altera a mensagem para que, além do stream, informe dados adicionais, como é mostrado no artigo e ressalto aqui:

[MessageContract]
public class StreamData
{
    [MessageHeader]
    public string NomeDoArquivo;

    [MessageHeader]
    public int Tamanho;

    [MessageBodyMember]
    public Stream Conteudo;
}

Ao retornar esse objeto, o stream relacionado a ele ficará aberto, causando problemas futuros. O WCF possui uma propriedade chamada AutoDisposeParameters, que está acessível através do atributo OperationBehaviorAttribute. Essa propriedade que, por padrão é True, diz ao runtime do WCF para invocar o método Dispose de todos os parametros (input e output) e dos objetos de retorno que implementam a Interface IDisposable e, a partir de agora, conseguimos entender porque quando lidamos com a classe Stream diretamente (ou uma de suas derivadas), o stream é devidamente fechado.

Como já deve suspeitar, o que nos resta fazer é também implementar a Interface IDisposable na classe StreamData e, dentro do método Dispose, invocar o método Dispose dos membros internos, garantindo assim, que o arquivo que está sendo utilizado por essa classe, seja fechado quando o método retornar.

[MessageContract]
public class StreamData : IDisposable
{
    [MessageHeader]
    public string NomeDoArquivo;

    [MessageHeader]
    public int Tamanho;

    [MessageBodyMember]
    public Stream Conteudo;

    public void Dispose()
    {
        if (this.Conteudo != null)
            this.Conteudo.Dispose();
    }
}