Fala pessoal, beleza?
Sensibilizado com os muitos prováveis mortais que vão esbarrar neste problema, resolvi postar um troubleshooting sobre o que aconteceu comigo ontem.
Cenário: Você está usando Entity Framework 4, gerou classes POCO através do modelo conceitual (com ou sem T4), está com Lazy Loading habilitado e quer distribuir estas classes em um serviço WCF.
Após decorar todas as classes e estar aparentemente pronto, tudo começa com uma bela e abrangente exceção do WCF:
“The underlying connection was closed: The connection was closed unexpectedly.”
Se você tiver esperança, como funcionou comigo, vai querer ligar o tracing do WCF. Existem inúmeras formas de configurá-lo, a que eu usei foi simples e good-enough:
<system.diagnostics> <sources> <source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true"> <listeners> <add name="traceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData= "c:\temp\NomeDoSeuArquivoDeLog.svclog" /> </listeners> </source> </sources> </system.diagnostics>
Adicione o trecho XML acima no Web.config do seu serviço e tudo estará pronto para começarmos a perseguir o verdadeiro problema. Se você está no mesmo cenário que eu estive e descrevi acima, vai encontrar no SvcTraceViewer.exe a seguinte mensagem da exceção:
“There was an error while trying to serialize parameter http://tempuri.org/:SUA_OPERACAO_AQUI. The InnerException message was ‘Type ‘System.Data.Entity.DynamicProxies.SUA_ENTIDADE_AQUI_7446BE543376C2D0E2C8BCE8BE89AC8659C6C2397A28812EBD37F48756075A0D’ with data contract name ‘SUA_ENTIDADE_AQUI_7446BE543376C2D0E2C8BCE8BE89AC8659C6C2397A28812EBD37F48756075A0D:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies’ is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types – for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.’. Please see InnerException for more details.”
A exceção acima esclarece bastante coisa. Entre linhas ela nos diz que o DataContractSerializer não sabe fazer resolução dos DynamicProxies gerados pelo Entity Framework (os DynamicProxies existem para que seja possível utilizar o recurso de Lazy Loading). A grosso modo, desabilitamos a criação de proxys do Lazy Loading ou interceptamos a criação do DataContractSerializer.
Opção 1: Desabilitar a criação de proxies para o Lazy-Loading
meuDataContext.ContextOptions.ProxyCreationEnabled = false;
Opção 2: Implementar um OperationBehavior que utilize o DataContractResolver e acioná-lo via atributo (nas operações do contrato do serviço).
- Crie um Assemly e referencie System.Data.Entity, System.Runtime.Serialization e System.ServiceModel
- Crie uma classe chamada ApplyDataContractResolverAttribute com o seguinte código:
public class ApplyDataContractResolverAttribute : Attribute, IOperationBehavior { public ApplyDataContractResolverAttribute() { } public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters) { } public void ApplyClientBehavior(OperationDescription description, System.ServiceModel.Dispatcher.ClientOperation proxy) { DataContractSerializerOperationBehavior dataContractSerializerOperationBehavior = description.Behaviors.Find<DataContractSerializerOperationBehavior>(); dataContractSerializerOperationBehavior.DataContractResolver = new ProxyDataContractResolver(); } public void ApplyDispatchBehavior(OperationDescription description, System.ServiceModel.Dispatcher.DispatchOperation dispatch) { DataContractSerializerOperationBehavior dataContractSerializerOperationBehavior = description.Behaviors.Find<DataContractSerializerOperationBehavior>(); dataContractSerializerOperationBehavior.DataContractResolver = new ProxyDataContractResolver(); } public void Validate(OperationDescription description) { // Do validation. } }Pronto! Agora compile o assembly, referencie-o no serviço e decore as operações do contrato (em que deseja trafegar POCOs com DynamicProxy):
[ServiceContract] public partial interface IMyService { // Operação com DynamicProxys do Entity Framework [OperationContract] [ApplyDataContractResolver] DynamicFooResponse DynamicFoo (DynamicFooRequest request); // Operação sem DynamicProxys do Entity Framework [OperationContract] FooResponse Foo (FooRequest request); }Guarde este assembly no seu WCF-Bag-Of-Tricks! Bom pessoal, existem milhares de artigos sobre este problema (inclusive na MSDN), mas não podia deixar de fazer a versão português-comentada de um problema tão importante quanto este. Até a próxima! Stay Lazy-Loaded!