Partager via


Preserving Object Reference in WCF

By default object references are not preserved by the DataContractSerializer; Values of an object referenced multiple times is serialized multiple times. If the object is part of mutual (cyclic) reference (e.g. circular linked list) an exception is thrown during serialization.

DataContractSerializer can be made to preserve object reference by passing true for parameter PreserveObjectReference when constructing DataContractSerializer as shown below.

new DataContractSerializer(type, name, ns, knownTypes,

                0x7FFF /*maxItemsInObjectGraph*/,

                false/*ignoreExtensionDataObject*/,

   true/*preserveObjectReferences*/,

                null/*dataContractSurrogate*/);

Enabling in Service Operation

To enable this option in service operation one must pass an instance of DataContractSerializer to the WCF runtime. This can be done by sub classing DataContractSerializerOperationBehavior and overriding CreateSerializer method as shown below.

    class ReferencePreservingDataContractSerializerOperationBehavior

      :DataContractSerializerOperationBehavior

    {

        public ReferencePreservingDataContractSerializerOperationBehavior(

          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*/);

        }

    }

The behavior must be added to all the operations on the server side contract. For a self hosted server it can be done as shown below.

        ServiceHost host = new ServiceHost(

                             typeof(FooContractImpl), new Uri(address));

        host.AddServiceEndpoint(typeof(FooContract), binding, address);

        foreach (ServiceEndpoint endpoint in host.Description.Endpoints)

            SetDataContractSerializerBehavior(endpoint.Contract);

        host.Open();

Similary the behavior must be added to all the operations on the client side as shown below.

        ChannelFactory<FooContract> factory = new ChannelFactory<FooContract>(binding, new EndpointAddress(address));

        SetDataContractSerializerBehavior(factory.Endpoint.Contract);

        FooContract proxy = factory.CreateChannel();

The SetDataContractSerializerBehavior is defined as shown below.

    private static void SetDataContractSerializerBehavior(ContractDescription contractDescription)

    {

        foreach (OperationDescription operation in contractDescription.Operations)

        {

    operation.Behaviors.Add(new ReferencePreservingDataContractSerializerOperationBehavior(operation));

        }

    }

Enabling via Attribute

This is all good. However wouldn’t it be great if it is possible avoid all the imperative code and instead define an attribute as shown below?

[ServiceContract]

public interface FooContract

{

    [OperationContract]

    [ReferencePreservingDataContractFormat]

    Node EchoNode(Node node);

}

Yes this can be done by defining a custom attribute that implements IOperationBehavior as shown below.

public class ReferencePreservingDataContractFormatAttribute : Attribute, IOperationBehavior

{

    #region IOperationBehavior Members

    public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)

    {

    }

    public void ApplyClientBehavior(OperationDescription description,System.ServiceModel.Dispatcher.ClientOperation proxy)

    {

        IOperationBehavior innerBehavior = new ReferencePreservingDataContractSerializerOperationBehavior(description);

        innerBehavior.ApplyClientBehavior(description, proxy);

    }

    public void ApplyDispatchBehavior(OperationDescription description,System.ServiceModel.Dispatcher.DispatchOperation dispatch)

    {

        IOperationBehavior innerBehavior = new ReferencePreservingDataContractSerializerOperationBehavior(description);

        innerBehavior.ApplyDispatchBehavior(description, dispatch);

    }

    public void Validate(OperationDescription description)

    {

    }

    #endregion

}

The complete sample is attached.

Program.cs

Comments

  • Anonymous
    April 07, 2006
    PingBack from http://www.douglasp.com/blog/PermaLink.aspx?guid=0cc78757-ae6c-4ead-b8c0-93ef69f56234

  • Anonymous
    April 07, 2006
    My grand boss ... if someone had told me this&amp;nbsp;a year back ... but it turns out that&amp;nbsp;it is a...

  • Anonymous
    April 08, 2006
    Для тех, кому за 30. Для тех, кто не любит модных технологий. Для тех кто презир

  • Anonymous
    April 08, 2006
    Aunque de manera predeterminada&#160;WCF no soporta el paso de referencias o el preservar grafos de objetos como se lo hac&#237;a con Remoting, esto no significa que no se lo pueda hacer. Si alguno de esto ...

  • Anonymous
    April 26, 2006
    It would make things easy if this could be configured for an entire app in the runtime.serialization section like so:

    <system.runtime.serialization>
      <dataContractSerializer preserveObjectReferences="true">
      </dataContractSerializer>
    </system.runtime.serialization>

  • Anonymous
    May 05, 2006
    The following links to .NET resources have been collated over time with the assistance of colleagues.&amp;nbsp;...

  • Anonymous
    March 11, 2007
    Introduction La technologie Windows Communication Foundation (WCF) propose deux méthodes en vue de définir

  • Anonymous
    May 18, 2007
    A natural progression from yesterday's article about creating a new serializer is to put that serializer

  • Anonymous
    July 22, 2007
    PingBack from http://whiletrue.nl/blog/?p=18

  • Anonymous
    August 30, 2007
    Il post è un po vecchiotto ma torna sempre utile: By default object references are not preserved by the

  • Anonymous
    November 02, 2007
    your code must be changed for 3.5, see here https://connect.microsoft.com/wcf/feedback/ViewFeedback.aspx?FeedbackID=307497&wa=wsignin1.0

  • Anonymous
    January 20, 2008
    Serialization possibilities in the .NET Framework have been significantly augmented with the advent of

  • Anonymous
    June 16, 2008
    PingBack from http://blogs.southworks.net/dperez/2008/06/16/preserving-object-reference-in-wcf/

  • Anonymous
    April 08, 2009
    Why is preserving object references not the default?

  • Anonymous
    June 09, 2009
    Bump.  Same question.   Why is preserving object references not the default?

  • Anonymous
    June 10, 2010
    Some little error in this article. In the 'SetDataContractSerializerBehavior' you must remove the old behavior first. like this:    private static void SetDataContractSerializerBehavior(ContractDescription contractDescription)    {        foreach (OperationDescription operation in contractDescription.Operations)        {            operation.Behaviors.Add(new ReferencePreservingDataContractSerializerOperationBehavior(operation));        }    }

  • Anonymous
    July 07, 2010
    sry wrong code,  i mean this: foreach (OperationDescription operation in client.Endpoint.Contract.Operations)            {                operation.Behaviors.Remove(typeof(DataContractSerializerOperationBehavior));                operation.Behaviors.Add(new DataContractSerializerOperationBehaviorEx(operation));            }

  • Anonymous
    November 22, 2010
    Why do you give a solution with a recursive stackoverflow function; private static XmlObjectSerializer CreateDataContractSerializer(          Type type, string name, string ns, IList<Type> knownTypes)        {            return CreateDataContractSerializer(type, name, ns, knownTypes);        } ?

  • Anonymous
    June 28, 2011
    please take al look at zamd.net/.../datacontract-serializer-and-isreference-property

  • Anonymous
    September 26, 2015
    What is it going to take to get Microsoft to properly document WCF Serialization/Deserialization ?