Dealing with Circular References in WCF

Using enti­ty class­es in your appli­ca­tion and WCF is com­plain­ing about the cir­cu­lar ref­er­ences between your class­es? Well, I had the exact same prob­lem not long ago, and I found this post on James Kovac’s blog about cir­cu­lar ref­er­ences and how to get around them:

http://www.jameskovacs.com/blog/GoingAroundInCirclesWithWCF.aspx

The key things to note from this post is that:

  1. WCF can han­dle cir­cu­lar ref­er­ences, but is not switched on by default
  2. There is a boolean flag in the con­struc­tor for the Dat­a­Con­tract­Se­ri­al­iz­er class which enables object ref­er­ences to be pre­served
  3. We can tell WCF to switch this on by deriv­ing from the Dat­a­Con­tract­Se­ri­al­ize­r­Op­er­a­tionBe­hav­iour class (as shown in the blog post above)

By this point you’re prob­a­bly won­der­ing why cir­cu­lar ref­er­ence han­dling is not enabled by default, accord­ing to James Kovac, it’s because WCF by default tries to respect inter­op­er­abil­i­ty safe­ty:

Now why can’t WCF han­dle cir­cu­lar ref­er­ences out-of-the-box. The rea­son is that there is no indus­try-accept­ed, inter­op­er­a­ble way of express­ing any­thing but par­ent-child rela­tion­ships in XML. You can use the ID/IDREF fea­ture of XML or the key/keyref fea­ture of XML Schema, but a lot of seri­al­iz­ers don’t respect these attrib­ut­es or han­dle them prop­er­ly. So if you want to seri­al­ize cir­cu­lar ref­er­ences, you need to stray out of the realm of safe inter­op­er­abil­i­ty.

So here are the class­es you need to cre­ate to extend Dat­a­Con­tract­Se­ri­al­ize­r­Op­er­a­tionBe­hav­iour in order to pre­serve object ref­er­ence:

public class PreserveReferencesOperationBehavior : DataContractSerializerOperationBehavior
{
     public PreserveReferencesOperationBehavior(OperationDescription operation) : base(operation)
     {
     }

     public PreserveReferencesOperationBehavior(
          OperationDescription operation, DataContractFormatAttribute dataContractFormatAttribute)
          : base(operation, dataContractFormatAttribute)
     {
     }

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

And the attribute to use on your oper­a­tion con­tract:

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

     public void ApplyClientBehavior(OperationDescription description, ClientOperation proxy)
     {
          IOperationBehavior innerBehavior = new PreserveReferencesOperationBehavior(description);
          innerBehavior.ApplyClientBehavior(description, proxy);
     }

     public void ApplyDispatchBehavior(OperationDescription description,
                                       DispatchOperation dispatch)
     {
          IOperationBehavior innerBehavior = new PreserveReferencesOperationBehavior(description);
          innerBehavior.ApplyDispatchBehavior(description, dispatch);
     }

     public void Validate(OperationDescription description)
     {
     }
}

which you apply like this:

[OperationContract]
[PreserveReferences]
MyClass RetrieveMyClass();