0

edit: As mentioned in my comment I found out the reason for this problem was that the Module object has a reference back to the OrderInfo object. DataContractSerializer don't support preserving the object references by default. I have now been able get all this to work correctly. If anyone is interested contact me and I'll add it in an answer here.

  • .net service to .net client with shared POCO (data objects) library at both ends.
  • Object OrderInfo contains a List. If the list contains any Module objects I get the dreaded "The underlying connection was closed: The connection was closed unexpectedly."
  • I can send a "standalone" List from another service method and it works fine so Module object by themselves serialize/deserialize fine.
  • I don't use datacontract in the POCO classes, WCF handles this automatically (which might also be the problem. I've tried adding:

    [Serializable]
    [XmlInclude(typeof(List<Module>))]
    

but that didn't help. I can't see what the problem is since I do THE EXACT SAME thing in the Module object returning a collection of Pricemodel objects.

    public class OrderInfo
    {
    int _ProductID;
    IList<Module> _Modules = new List<Module>();
    //IList<MiscProduct> _MiscProduct = new List<MiscProduct>();

    public IList<Module> Modules
    {
        get
        {
            return new List<Module>(_Modules).AsReadOnly();
        }
        set
        {
            _Modules = value;
        }
    }
    }

    public class Module
    {
    string _Name;
    int _Sort_Number;
    string _Description;
    OrderInfo _OrderInfoMaster;
    IList<Pricemodel> _Pricemodels = new List<Pricemodel>();

    public IList<Pricemodel> Pricemodels
    {
        get
        {
            return new List<Pricemodel>(_Pricemodels).AsReadOnly();
        }
        set
        {
            _Pricemodels = value;
        }
    }
    }

Calling client code is:

    using (ProductOrderItems_WCFService.ProductOrderServiceClient client = new ProductOrderItems_WCFService.ProductOrderServiceClient())
    {
        string s = client.HelloWorld();
        Module m = client.GetModule();
        List<Module> mods = client.GetModuleList(7);
        grdModules.DataSource = mods;
        grdModules.DataBind();

        OrderInfo oi = client.GetOrderInfo(7);
    }

It fails on the last line when I request OrderInfo object from service. All the above calls work great.

Trygve
  • 2,445
  • 1
  • 19
  • 23
  • I managed to figure out that it occurs because the Module object has a reference "back" to the parent OrderInfo object. I've tried to enable "PreserveObjectReferences" in a custom DataContractSerializerOperationBehavior as outlined in the end of this article: http://www.zamd.net/2008/05/20/DataContractSerializerAndIsReferenceProperty.aspx But I couldn't get it to work and have given up for the moment. I didn't wan't to use any attributes in my POCO classes but I might give that a shot with the [DataContract(IsReference = true)] on my classes. I believe the WCF team should make this easier! – Trygve Apr 08 '10 at 13:39
  • yep, that's pretty much my issue right now as well. Don't to mark up the POCO classes with anything, but the way Entity Framework loads up the navigation properties, I simply cannot get away from trying to serialize a circular reference. I posted a question here: http://stackoverflow.com/questions/2915181/ef4-poco-wcf-serialization-problems-no-lazy-loading-proxy-no-proxy-circular-re – kdawg May 26 '10 at 19:06

2 Answers2

0

First the custom DataContactSerializerOperationBehavior

using System;
using System.ServiceModel.Description;
using System.Runtime.Serialization;
using System.Collections.Generic;

/// <summary>
/// Summary description for ReferencePreservingDataContractSerializerOperationBehavior
/// </summary>
public class ReferencePreservingDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior
{
    public ReferencePreservingDataContractSerializerOperationBehavior(OperationDescription operation) : base(operation)
    {
    }

    public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
    {
        return new DataContractSerializer(type, name, ns, knownTypes, this.MaxItemsInObjectGraph, this.IgnoreExtensionDataObject, true, this.DataContractSurrogate);
    }

    public override XmlObjectSerializer CreateSerializer(Type type, System.Xml.XmlDictionaryString name, System.Xml.XmlDictionaryString ns, IList<Type> knownTypes)
    {
        return new DataContractSerializer(type, name, ns, knownTypes, this.MaxItemsInObjectGraph, this.IgnoreExtensionDataObject, true, this.DataContractSurrogate);
    }
}

Next is the SelfDescribingServiceHost to allow us to use the ReferencePreservingDataContractSerializerOperationBehavior

using System;
using System.ServiceModel;
using System.ServiceModel.Description;

namespace NewWcfService
{
    //This class is a custom derivative of ServiceHost
    //that can automatically enabled metadata generation
    //for any service it hosts.
    class SelfDescribingServiceHost : ServiceHost
    {
        public SelfDescribingServiceHost(Type serviceType, params Uri[] baseAddresses)
            : base(serviceType, baseAddresses)
        {
        }

        //Overriding ApplyConfiguration() allows us to 
        //alter the ServiceDescription prior to opening
        //the service host. 
        protected override void ApplyConfiguration()
        {
            //First, we call base.ApplyConfiguration()
            //to read any configuration that was provided for
            //the service we're hosting. After this call,
            //this.ServiceDescription describes the service
            //as it was configured.
            base.ApplyConfiguration();

            foreach (ServiceEndpoint endpoint in this.Description.Endpoints)
                SetDataContractSerializerBehavior(endpoint.Contract);

            //Now that we've populated the ServiceDescription, we can reach into it
            //and do interesting things (in this case, we'll add an instance of
            //ServiceMetadataBehavior if it's not already there.
            ServiceMetadataBehavior mexBehavior = this.Description.Behaviors.Find<ServiceMetadataBehavior>();
            if (mexBehavior == null)
            {
                mexBehavior = new ServiceMetadataBehavior();
                this.Description.Behaviors.Add(mexBehavior);
            }
            else
            {
                //Metadata behavior has already been configured, 
                //so we don't have any work to do.
                return;
            }

            //Add a metadata endpoint at each base address
            //using the "/mex" addressing convention
            foreach (Uri baseAddress in this.BaseAddresses)
            {
                if (baseAddress.Scheme == Uri.UriSchemeHttp)
                {
                    mexBehavior.HttpGetEnabled = true;
                    this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,
                                            MetadataExchangeBindings.CreateMexHttpBinding(),
                                            "mex");
                }
                else if (baseAddress.Scheme == Uri.UriSchemeHttps)
                {
                    mexBehavior.HttpsGetEnabled = true;
                    this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,
                                            MetadataExchangeBindings.CreateMexHttpsBinding(),
                                            "mex");
                }
                else if (baseAddress.Scheme == Uri.UriSchemeNetPipe)
                {
                    this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,
                                            MetadataExchangeBindings.CreateMexNamedPipeBinding(),
                                            "mex");
                }
                else if (baseAddress.Scheme == Uri.UriSchemeNetTcp)
                {
                    this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,
                                            MetadataExchangeBindings.CreateMexTcpBinding(),
                                            "mex");
                }
            }

        }

        private static void SetDataContractSerializerBehavior(ContractDescription contractDescription)
        {
            foreach (OperationDescription operation in contractDescription.Operations)
            {
                DataContractSerializerOperationBehavior dcsob = operation.Behaviors.Find<DataContractSerializerOperationBehavior>();
                if (dcsob != null)
                {
                    operation.Behaviors.Remove(dcsob);
                }
                operation.Behaviors.Add(new ReferencePreservingDataContractSerializerOperationBehavior(operation));
            }
        }
    }
}

Then there is the ServiceHostFactory:

using System;
using System.ServiceModel;
using System.ServiceModel.Activation;

namespace NewWcfService
{
        public class SelfDescribingServiceHostFactory : ServiceHostFactory
    {
        protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
        {
            //All the custom factory does is return a new instance
            //of our custom host class. The bulk of the custom logic should
            //live in the custom host (as opposed to the factory) for maximum
            //reuse value.
            return new SelfDescribingServiceHost(serviceType, baseAddresses);
        }
    }
}

And of course the Service.svc to use the new HostFactory: <%@ ServiceHost Language="C#" Debug="true" Service="NewWcfService.Service" Factory="ProTeriaWCF.SelfDescribingServiceHostFactory" CodeBehind="Service.svc.cs" %>

Trygve
  • 2,445
  • 1
  • 19
  • 23
0

There can be two reasons of this error;

  1. If your return object has recursive objects, I mean, your return object and inside objects includes each other, than it creates serialization problem. You need to cut recursive objects at a level that you can decide.

  2. Enumerations with value 0 can cause this problem.

NetSide
  • 3,849
  • 8
  • 30
  • 41
  • 1. Agreed, is an issue if the recursion is too deep. 2. I wasn't aware of that – Trygve Apr 04 '11 at 10:28
  • For example, if you try to return below enum of value 0, it throws same error. Because value 0 is unidentified. public enum StateIdentity { Deleted = 1, Rejected = 2, Paid = 3, Passive = 4, } – NetSide Apr 04 '11 at 14:43