WCF – Known Types


É muito comum em qualquer linguagem orientada a objetos, criarmos uma classe base e que, a partir dela, criar classes derivadas. Além disso, um dos grandes benefícios que temos com a orientação a objetos é a possibilidade de declararmos uma variável do tipo da classe base e atribuirmos a ela uma instância de uma classe concreta e, da mesma forma, podemos ter uma função em que em seus parâmetros os seus tipos são especificados com o tipo da classe base e, conseqüentemente, podemos também passar instâncias das classes derivadas.

Infelizmente não funciona da mesma forma quando falamos de serviços que são expostos a partir do WCF. Neste cenário, por padrão, você não pode usar uma classe derivada ao invés de uma classe base. Sendo assim, se quisermos utilizar esta classe base publicamente (parâmetros e retorno de métodos), precisamos nos atentar em algumas técnicas para permitir isso. Para exemplificar o problema, vamos analisar o código contrato abaixo:

using System;
using System.ServiceModel;

namespace DevMinds.Library
{
    [ServiceContract]
    public interface IGerenciadorDeContatos
    {
        [OperationContract]
        void AdicionarContato(Pessoa p);

        [OperationContract]
        Pessoa[] RecuperaContatos();
    }
}

E, além do contrato, temos a classe Pessoa que possui apenas uma propriedade do tipo string:

using System;
using System.ServiceModel;
using System.Runtime.Serialization;

namespace DevMinds.Library
{
    [DataContract]
    public class Pessoa
    {
        private string _nome;
        
        [DataMember]
        public string Nome
        {
            get
            {
                return this._nome;
            }
            set
            {
                this._nome = value;
            }
        }
    }
}

Supondo-se que o cliente defina uma classe chamada Fisica (de pessoa física) que herde diretamente da classe Pessoa e tente enviar a instância desta classe para o método AdicionarContato. Apesar de compilar, você terá uma exceção quando o código for executado, pelo fato de que quando você passar a classe Fisica ao invés de Pessoa o serviço não saberá como deserializar a “Pessoa” que é recebida. O mesmo vale para quando você tem uma coleção em seu serviço de uma classe derivada e tenta retorná-la para o cliente, expondo através do retorno do método uma coleção de tipos base.

Para suavizar este problema, o WCF introduziu um atributo chamado KnownType. Esse atributo recebe em seu construtor um Type, indicando à infra-estrutura do WCF que existe uma classe derivada do tipo onde o atributo é aplicado e que ela também pode ser aceita. Quando você define este atributo do lado do servidor, você permitirá que todos os contratos e operações que utilizem este tipo base possam aceitar o tipo especificado pelo atributo. O exemplo abaixo ilustra como devemos proceder para utilizar o atributo KnownType:

[DataContract]
[KnownType(typeof(Fisica))]
public class Pessoa
{
    //Implementação
}

[DataContract]
public class Fisica : Pessoa
{
    //Implementação
}

Uma vez aplicado este atributo, ele fará com que a classe derivada seja adicionada nos metadados do serviço e, conseqüentemente, o cliente terá a definição da mesma, podendo passá-la para o serviço ao invés da classe base. Desta forma, podemos tranquilamente executar o código que antes do atributo era impossível:

using (GerenciadorDeContatosClient proxy = new GerenciadorDeContatosClient())
{
    Fisica f = new Fisica();
    f.Nome = "Israel";
    f.Cpf = "00000000000";
    proxy.AdicionarContato(f);
}

Como disse anteriormente, o ponto negativo deste atributo é com relação ao escopo, pois ele permite que todos os locais onde aceite uma classe base, aceitar um tipo derivado especificado no atributo. Se não quisermos isso, ou seja, se desejarmos habilitar este recurso somente para uma determinada operação, um contrato ou um serviço, podemos utilizar o atributo ServiceKnowType, que pode ser aplicado a um método, Interface ou classe. Com isso, o nosso código mudará ligeiramente, permitindo que somente um determinado método aceite instâncias da classe Fisica:

using System;
using System.ServiceModel;
using System.Runtime.Serialization;

namespace DevMinds.Library
{
    [ServiceContract]
    public interface IGerenciadorDeContatos
    {
        [OperationContract]
        [ServiceKnowType(typeof(Fisica))]
        void AdicionarContato(Pessoa p);

        //outros métodos
    }
}

E ainda, se quiser permitir mais de uma classe derivada de um tipo base, então poderá adicionar múltiplos atributos (KnowType ou ServiceKnowType) para satisfazer a todos os tipos que a operação poderá aceitar/retornar. O trecho de código abaixo ilustra múltiplos atributos para mais de um tipo derivado:

using System;
using System.ServiceModel;
using System.Runtime.Serialization;

namespace DevMinds.Library
{
    [ServiceContract]
    public interface IGerenciadorDeContatos
    {
        [OperationContract]
        [ServiceKnowType(typeof(Fisica))]
        [ServiceKnowType(typeof(Juridica))]
        void AdicionarContato(Pessoa p);

        //outros métodos
    }
}

Para finalizar, ainda temos um detalhe importante quando falamos de know types que é em relação à recompilação do código cliente ou do serviço quando alguma nova classe derivada deve ser informada para que ela possa ser utilizada. Uma vez que um novo tipo precisa ser utilizado, implica em mudar o código, recompilá-lo e redistribuí-lo. Para amenizar isso, o WCF permite-nos fazer essa configuração, ou melhor, adição de novos tipos, a partir do arquivo de configuração, como é mostrado através do trecho de código abaixo:

<system.runtime.serialization>
  <dataContractSerializer>
    <declaredTypes>
      <add type="DevMinds.Library.Pessoa, DevMinds.Library, Version=1.0.0.0, PublicKeyToken=null">
        <knownType
          type="DevMinds.Client.Juridica, DevMinds.Client, Version=1.0.0.0, PublicKeyToken=null"/>
      </add>
    </declaredTypes>
  </dataContractSerializer>
</system.runtime.serialization>

Conclusão: Mais uma vez vimos através de uma pequena funcionalidade o quanto o WCF é flexível e permite de uma forma bem fácil e simples integrar o código e seus tipos que são desenhados e executados no cliente com o código que temos no servidor.

Anúncios

Um comentário sobre “WCF – Known Types

  1. Olá. Parabéns pelo artigo. Muito mais simples de entender, em relação ao MSDN. Seria interessante também, demonstrar o uso de KnownTypes com collections e generics.

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s