Ao construir serviços WCF, podemos expor tipos que vão além dos tipos tradicionais (tais como strings, inteiros, etc.), ou seja, podemos recorrer a construção de tipos customizados, onde construimos classes com suas propriedades que determinam os dados que serão carregados entre o cliente e o serviço.
Algumas vezes essas classes são construídas com o intuito de atender a requisição e/ou resposta das operações de um determinado serviço, mas que internamente, são consultas que fazemos no banco de dados e estamos retornando ao cliente, ou ainda, são comandos que devemos executar, também no banco de dados, para persitir alguma informação.
Se optarmos pelo uso do Entity Framework, podemos utilizar o modelo de geração POCO, onde as classes geradas são consideradas “puras”, ou seja, não dependem de nenhuma classe do framework de persistência. Isso tornam as classes simples, apenas com as propriedades que são, em princípio, mapeadas para colunas da base de dados. Sendo assim, muitos podem optar pela exposição direta destas classes no contrato de um serviço WCF, evitando assim a criação de outras classes (DTOs) para efetuar o transporte das informações.
Mas expor diretamente essas classes podem acarretar alguns problemas. A questão é que entidades que foram geradas utilizando o modelo POCO, possuem um comportamento durante a execução diferente das entidades que são geradas pelo modelo padrão do Entity Framework. Quando utilizamos o modelo padrão, as entidades herdam diretamente da classe EntityObject, que fornece toda a infraestrutura necessária para efetuar o rastreamento das mudanças e dá também o suporte ao lazy-loading. Quando utilizamos classes POCO, esses recursos continuam sendo oferecidos, mas são implementados de forma diferente.
Neste caso, o Entity Framework gera proxies dinâmicos para interceptar os acessos às propriedades das entidades que estão sendo manipuladas, e com isso, todos os recursos acima, continuam ainda sendo suportados. Abaixo temos o objeto Type da classe que foi gerada dinamicamente.
{System.Data.Entity.DynamicProxies.Cliente_B38C1C71074F1A0CDCF11548021F55AF4BAC2116057847A45ACD8E600D02BB90}
Só que ao expor esse tipo de objeto diretamente através do WCF, vamos nos deparar com uma exceção durante a execução. Isso se deve ao fato de que o WCF não é capaz serializar/deseralizar tipos que não são conhecidos pelo mesmo. Ao construir o contrato para um serviço WCF, você pode mencionar os tipos das classes geradas efetivamente pelo EDMX, mas durante a execução, o tipo acima será criado e, consequentemente, será entregue para a infraestrutura do WCF, ele irá se certificar de que o tipo é conhecido no contrato, e como não é, a exceção será disparada.
Há algumas alternativas para evitar esse problema. A primeira delas é desabilitar a criação do proxy dinâmico através da propriedade boleana ProxyCreationEnabled, que está acessível através do contexto do Entity Framework, assim como é exibido no código abaixo. O problema é que ao fazer isso, entidades criadas no padrão POCO não serão capazes de fornecer as funcionalidades de rastreamento de mudanças e lazy-loading.
public class Servico : IContrato
{
public Cliente Recuperar(int id)
{
using (DBTestesEntities1 ctx = new DBTestesEntities1())
{
ctx.ContextOptions.ProxyCreationEnabled = false;
return (from c in ctx.Cliente where c.ClienteId == id select c).Single();
}
}
}
A outra opção é a criação de um behavior que pode ser aplicado em nível de operação, alterando o serializador padrão do WCF, que é o DataContractSerializer, para o serializador ProxyDataContractResolver. Este serializador, que está debaixo do namespace System.Data.Objects, ajuda na resolução dos tipos que são criados dinamicamente pelo Entity Framework em “tipos concretos”, recorrendo ao método estático GetObjectType, exposto através da classe ObjectContext, que dado o Type do proxy dinâmico, ele retorna o tipo correspondente POCO, informando ao WCF que Type correto para que ele possa ser capaz de serializá-lo. Abaixo temos um exemplo da implementação e aplicação deste behavior:
public class ProxyDataResolverAttribute : Attribute, IOperationBehavior
{
public void ApplyDispatchBehavior(OperationDescription opDescription, DispatchOperation dop)
{
opDescription
.Behaviors
.Find<DataContractSerializerOperationBehavior>()
.DataContractResolver = new ProxyDataContractResolver();
}
//outros métodos
}
[ServiceContract]
public interface IContrato
{
[OperationContract]
[ProxyDataResolver]
Cliente Recuperar(int id);
}
E, finalmente, uma terceira opção, seria a utilização de DTOs (Data Transfer Objects), onde você pode definir os objetos que quer trafegar entre as partes, sem a necessidade de se preocupar com detalhes de uma tecnologia específica que esteja utilizando nos bastidores e, principalmente, de ter a possibilidade de desacoplar complementamente o que representa os seus dados/domínio daquilo que quer expor para os clientes/parceiros.
A melhor opção nesse caso (WCF, ChangeTracker e EF) não seria usar POCO STE (Self Tracking Entities) ???
Boas Hamilton,
Talvez sim. O problema do STE é a quantidade de informações que trafegam na rede. Eu ainda prefiro utilizar DTOs.