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.
Comments
Anonymous
April 07, 2006
PingBack from http://www.douglasp.com/blog/PermaLink.aspx?guid=0cc78757-ae6c-4ead-b8c0-93ef69f56234Anonymous
April 07, 2006
My grand boss ... if someone had told me this&nbsp;a year back ... but it turns out that&nbsp;it is a...Anonymous
April 08, 2006
Для тех, кому за 30. Для тех, кто не любит модных технологий. Для тех кто презирAnonymous
April 08, 2006
Aunque de manera predeterminada WCF no soporta el paso de referencias o el preservar grafos de objetos como se lo hací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.&nbsp;...Anonymous
March 11, 2007
Introduction La technologie Windows Communication Foundation (WCF) propose deux méthodes en vue de définirAnonymous
May 18, 2007
A natural progression from yesterday's article about creating a new serializer is to put that serializerAnonymous
July 22, 2007
PingBack from http://whiletrue.nl/blog/?p=18Anonymous
August 30, 2007
Il post è un po vecchiotto ma torna sempre utile: By default object references are not preserved by theAnonymous
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.0Anonymous
January 20, 2008
Serialization possibilities in the .NET Framework have been significantly augmented with the advent ofAnonymous
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-propertyAnonymous
September 26, 2015
What is it going to take to get Microsoft to properly document WCF Serialization/Deserialization ?