fonix232
fonix232

Reputation: 2195

WCF Service Invocation Error - Error while receiving HTTP response

I'm almost finished with writing my service, and I've spend the past few hours testing. However, no matter how hard I try, I get an error with one specific service's one specific method (code visible here)

Everything works fine, except for the Get() and GetAll() methods of Cinema.Server.ShowController. As you can see from the code, Show is a complex object, and I require its transfer, the architecture cannot change.

Any time I invoke these two functions, if there's a database entry for any Show, it will throw the following error message:

An error occurred while receiving the HTTP response to http://localhost:8090/ShowService. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details.

(Full message with stack trace available here)

As there is no error report from WCF apart from this (and this exception is thrown on client-side), I do not know what the problem is. Could someone enlighten me please?

UPDATE

So after some SvcTracing, here is the actual exception:

There was an error while trying to serialize parameter http://tempuri.org/:GetAllResult. The InnerException message was 'Type 'System.Data.Entity.DynamicProxies.Show_B50FB72FE59AC01FA537205B27B30F94605CB1F39BB27C2F1BBB00EF9A720361' with data contract name 'Show_B50FB72FE59AC01FA537205B27B30F94605CB1F39BB27C2F1BBB00EF9A720361: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.

Upvotes: 0

Views: 3945

Answers (3)

David
David

Reputation: 4963

I think this part of your error:

'Type 'System.Data.Entity.DynamicProxies.Show_B50FB72FE59AC01FA537205B27B30F94605CB1F39BB27C2F1BBB00EF9A720361' with data contract name 'Show_B50FB72FE59AC01FA537205B27B30F94605CB1F39BB27C2F1BBB00EF9A720361:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies' is not expected.

may be addressed by setting ProxyCreationEnabled to false on your context, like this:

cx = new CinemaContext();
cx.Configuration.ProxyCreationEnabled = false;

There may certainly be more to it than that, but that may help clarify the actual problem.

Then, you may look at this post which talks about resolving circular references by using DataContract/DataMember. I added an answer there that shows you how to do it in a separate file. See if that helps you out.

Upvotes: 1

Robert Graves
Robert Graves

Reputation: 2320

You are using Entity Framework inside your WCF service, and you are returning the entity retrieved from the database. EF dynamically creates a proxy subclass of your class. WCF doesn't know how to handle the proxy subclass.

This article has more detials: http://msdn.microsoft.com/en-us/library/vstudio/ee705457(v=vs.100).aspx

The POCO proxy type cannot be directly serialized or deserialized by the Windows Communication Foundation (WCF), because the DataContractSerializer serialization engine can only serialize and deserialize known types. The proxy type is not a known type. For more information, see the Serializing POCO Proxies section in the Working with POCO Entities topic. To serialize POCO proxies as POCO entities, use the ProxyDataContractResolver class to map proxy types to POCO types during serialization.

For some reason MS gives a ProxyDataContractResolver, but not an attribute to apply the resolver. The article describes implementing a ApplyDataContractResolverAttribute and how to apply it to operations.

Update:

There is a related issue with returning EF objects directly through WCF when dealing with circular references. Say class A has a many to one relationship with class B. A has a navigation property to B, and B has a collection of A. This can be handled by marking the DataContract with the attribute [DataContract(IsReference=true)], but if you're passing EF objects back you may not want to add DataContract and DataMember to each class in the model. Instead, The DataContractSerializer can handle this for you by setting the preserveObjectReferences to true. Sowmy Srinivasan has an excellent article describing how to do this: Preserving Object Reference in WCF

Combining the two aproaches of using the ProxyDataContractResolver and the preserveObjectReferences = true, I came up with these classes:

public class ReferencePreservingProxyDataContractSerializerOperationBehavior
    : DataContractSerializerOperationBehavior
{
    public ReferencePreservingProxyDataContractSerializerOperationBehavior(
      OperationDescription operationDescription)
        : base(operationDescription) { }
    public override XmlObjectSerializer CreateSerializer(
      Type type, string name, string ns, IList<Type> knownTypes)
    {
        return CreateDataContractSerializer(type, name, ns, knownTypes);
    }

    private static XmlObjectSerializer CreateDataContractSerializer(
      Type type, string name, string ns, IList<Type> knownTypes)
    {
        return CreateDataContractSerializer(type, name, ns, knownTypes);
    }

    public override XmlObjectSerializer CreateSerializer(
      Type type, XmlDictionaryString name, XmlDictionaryString ns,
      IList<Type> knownTypes)
    {
        return new DataContractSerializer(type, name, ns, knownTypes,
            0x7FFF /*maxItemsInObjectGraph*/,
            false/*ignoreExtensionDataObject*/,
            true/*preserveObjectReferences*/,
            null/*dataContractSurrogate*/,
            new ProxyDataContractResolver());
    }
}

public class ReferencePreservingProxyDataContractFormatAttribute : Attribute, IOperationBehavior
{
    public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
    {
    }

    public void ApplyClientBehavior(OperationDescription description, System.ServiceModel.Dispatcher.ClientOperation proxy)
    {
        IOperationBehavior innerBehavior = new ReferencePreservingProxyDataContractSerializerOperationBehavior(description);
        innerBehavior.ApplyClientBehavior(description, proxy);
    }

    public void ApplyDispatchBehavior(OperationDescription description, System.ServiceModel.Dispatcher.DispatchOperation dispatch)
    {
        IOperationBehavior innerBehavior = new ReferencePreservingProxyDataContractSerializerOperationBehavior(description);
        innerBehavior.ApplyDispatchBehavior(description, dispatch);
    }

    public void Validate(OperationDescription description)
    {
    }
}

Then for each operation, I just apply [ReferencePreservingProxyDataContractFormatAttribute]

Upvotes: 2

simon at rcl
simon at rcl

Reputation: 7344

Put this in the client's config file:

  <system.diagnostics>
    <sources>
      <source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true">
        <listeners>
          <add name="traceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData="Logger.xml" />
        </listeners>
      </source>
    </sources>

  </system.diagnostics>

Run the client a few times getting the error, and confirm that there is a logging.xml file in the exe's folder.

Download the Windows SDK for your OS, and run SvcTraceViewer, pointing it at the xml file. If this doesn't help, put the XML in the server's config and repeat.

It's not guaranteed to give the answer, but can help tremendously!

One pitfall I came across with complex structures in WCF is circular references: an onject contains a list of children; each child contains a reference (serialised as a copy) of its parent. The serialised copy of the parent contains a list of children etc etc.

Cheers -

Upvotes: 2

Related Questions