Surrogates para DataContracts


Como já é sabido, podemos expor um serviço WCF que utiliza tipos complexos, ou seja, uma classe customizada, contendo as propriedades que a descrevem. Com isso, basta referenciá-la em nosso contrato e, consequentemente, os tipos utilizados serão expostos nos metadados (WSDL) do serviço, permitindo aos clientes criar uma representação desta classe. Para exemplificar, eis a classe com a Interface que descreve o contrato do serviço:

[DataContract]
public class Usuario
{
    [DataMember]
    public string Nome { get; set; }
}

[ServiceContract]
public interface IContrato
{
    [OperationContract]
    Usuario Recuperar(Usuario u);
}

Imagine que por algum motivo, voce cria um novo tipo, chamado OutroUsuario e quer fazer o uso deste no contrato, mudando tanto o tipo do retorno quanto o tipo do parametro u do método Recuperar. Com esta mudança, o WSDL continuará referenciando o tipo antigo Usuario, não “quebrando” as referencias atuais. A idéia é alterar o tipo apenas do lado do servidor, não refletindo do lado do cliente. O código abaixo ilustra o tipo OutroUsuario, que será utilizado pelo serviço ao invés do tipo Usuario:

[DataContract(Name = “Usuario”)]
public class OutroUsuario
{
    public string XNome { get; set; }
    public string Dummy { get; set; }
}

[ServiceContract]
public interface IContrato
{
    [OperationContract]
    OutroUsuario Recuperar(OutroUsuario u);
}

Da forma que o contrato está, novas referencias utilizarão o tipo OutroUsuario e, além disso, os clientes atuais mandarão uma instancia de um tipo não conhecido pelo contrato (Usuario) e, não será possível efetuar a deserialização.

Felizmente o WCF disponibiliza um recurso chamado surrogate (substituto). Com a sua utilização, podemos interceptar e customizar a serialização, deserialização e a projeção dos metadados, podendo inclusive substituir um tipo por outro. Para criar um surrogate, é necessário criar uma classe e implementar a Interface IDataContractSurrogate. Entre os principais métodos, temos: GetDataContractType, GetDeserializedObject e GetObjectToSerialize.

O método GetDataContractType é responsável por mapear um tipo em outro, sendo invocado na serialização, deserialização e na importação e exportação dos metadados do serviço. Já o método GetDeserializedObject é invocado quando acontecer a deserialização de um objeto (cliente para o serviço). E, finalmente, o método GetObjectToSerialize é invocado apenas na serialização do objeto (serviço para o cliente). Abaixo temos a implementação do surrogate para exemplo:

public class SurrogateParaUsuario : IDataContractSurrogate
{
    public Type GetDataContractType(Type type)
    {
        return type == typeof(OutroUsuario) ? typeof(Usuario) : type;
    }

    public object GetDeserializedObject(object obj, Type targetType)
    {
        Usuario u = obj as Usuario;
        if (u != null)
            return new OutroUsuario() { Dummy = “ValorPadrao”, XNome = u.Nome };

        return obj;
    }

    public object GetObjectToSerialize(object obj, Type targetType)
    {
        OutroUsuario o = obj as OutroUsuario;
        if (o != null)
            return new Usuario() { Nome = o.XNome + o.Dummy };

        return obj;
    }

    //outros métodos
}

Quando um cliente invocar a operação Recuperar, ele passará a instancia de uma classe Usuario. Quando a mensagem chegar até o serviço, o método GetDataContractType será executado, devendo retornar um Type que representará o tipo em que o objeto deverá ser convertido que, no nosso caso, será o tipo OutroUsuario. Esse método é executado para cada tipo encontrado na operação, descartando os tipos primitivos (string, int, etc.).

Ao analisar o método GetDeserializedObject vemos que a instancia do parametro obj é do tipo Usuario e, neste momento, fazemos o mapeamento do tipo Usuario para o tipo OutroUsuario, mantendo os valores das propriedades fornecidas pelo cliente. Já no método GetObjectToSerialize fazemos o processo inverso, já que o nosso cliente espera a instancia de um objeto Usuario.

Essa classe por si só não funciona. Existe um behavior chamado DataContractSerializerOperationBehavior que nos permite interagir com o serializador do WCF. Ele por sua vez, fornece uma propriedade chamada DataContractSurrogate que recebe uma instancia de uma classe que implemente a Interface IDataContractSurrogate e deverá ser configurada antes da abertura do host, como mostrado abaixo:

host.AddServiceEndpoint(typeof(IContrato), new WSHttpBinding(), “srv”);

OperationDescription od = host.Description.Endpoints[0].Contract.Operations[0];
od.Behaviors.Find<DataContractSerializerOperationBehavior>().DataContractSurrogate =
    new SurrogateParaUsuario();

host.Open();

Anúncios

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