Zulfiqar's weblog

Architecture, security & random .Net

DataContract Serializer and IsReference property

Posted by zamd on May 20, 2008


In .net Framework 3.5 SP1, DataContractSerializer supports by-ref object graph serialization by using the standard xsd:ID/xsd:IDREF attributes. 

You can set the IsReference=true on your DataContract definition and serializer will generate XML elements with IDs/IDREFs attributes and will link them together rather embedding them inside each other(default behavior). 

Also if you examine the XSD generated by WCF as part of the metadata export, it will also contain the standard ID/IDREF xml schema attributes. Because of this, xml can be correctly parsed and understood by any framework in a standard way. 

This change will enable serialization of object graphs having circular references (which wasn’t possible previously – at least not without writing custom code) and will also reduce the size of the serialized xml. 

Let’s examine this change using the following DataContract definition: 

    [DataContract] 

    public class Employee 

    { 

        [DataMember] 

        public string Name { get; set; } 

        [DataMember] 

        public Employee Manager { get; set; } 

    } 

    [DataContract] 

    public class Department 

    { 

        [DataMember] 

        public List<Employee> Staff { get; set; } 

        [DataMember] 

        public string DeptName { get; set; } 

    } 

Now if we serialize following Department object using DataContractSerializer 

        var kenny = new Employee() { Name = “Kenny” }; 

        var bob = new Employee() { Name = “Bob”, Manager = kenny }; 

        var alice = new Employee() { Name = “Alice”, Manager = kenny }; 

        var ahmed = new Employee() { Name = “Ahmed”, Manager = kenny }; 

   

        var dept = new Department() { DeptName = “RandD”, Staff = new List<Employee>() { kenny, bob, alice, ahmed } };         

        DataContractSerializer dcs = new DataContractSerializer(typeof(Department));         

        var ms = new MemoryStream(); 

        dcs.WriteObject(ms, dept); 

        ms.Seek(0, SeekOrigin.Begin); 

          

        var sr = new StreamReader(ms); 

        var xml = sr.ReadToEnd(); 

We will get this xml. 

<Department xmlns=http://schemas.datacontract.org/2004/07/ConsoleApplication2 xmlns:i=http://www.w3.org/2001/XMLSchemainstance> 

      <DeptName>RandD</DeptName> 

      <Staff> 

            <Employee> 

                  <Manager i:nil=true /> 

                  <Name>Kenny</Name> 

            </Employee> 

            <Employee> 

                  <Manager> 

                        <Manager i:nil=true /> 

                        <Name>Kenny</Name> 

                  </Manager>  

                  <Name>Bob</Name> 

            </Employee> 

            <Employee> 

                  <Manager> 

                        <Manager i:nil=true /> 

                        <Name>Kenny</Name> 

                  </Manager>  

                  <Name>Alice</Name> 

            </Employee> 

            <Employee> 

                  <Manager> 

                        <Manager i:nil=true /> 

                        <Name>Kenny</Name> 

                  </Manager>  

                  <Name>Ahmed</Name> 

            </Employee> 

      </Staff> 

</Department> 

You can see manager Kenny is included in all Employee objects, essentially a by-value inclusion.  Now if we change the declaration of Employee class to following: 

    [DataContract(IsReference = true)] 

    public class Employee 

    { 

         [DataMember] 

        public string Name { get; set; } 

        [DataMember] 

        public Employee Manager { get; set; } 

    } 

With above change, you will get following different xml. 

<Department xmlns=http://schemas.datacontract.org/2004/07/ConsoleApplication2 xmlns:i=http://www.w3.org/2001/XMLSchemainstance> 

      <DeptName>R&D</DeptName> 

      <Staff> 

            <Employee z:Id=i1 xmlns:z=http://schemas.microsoft.com/2003/10/Serialization/> 

                  <Manager i:nil=true />  

                  <Name>Kenny</Name> 

            </Employee> 

            <Employee z:Id=i2 xmlns:z=http://schemas.microsoft.com/2003/10/Serialization/> 

                  <Manager z:Ref=i1 />  

                  <Name>Bob</Name> 

            </Employee> 

            <Employee z:Id=i3 xmlns:z=http://schemas.microsoft.com/2003/10/Serialization/> 

                  <Manager z:Ref=i1 />  

                  <Name>Alice</Name> 

            </Employee> 

            <Employee z:Id=i4 xmlns:z=http://schemas.microsoft.com/2003/10/Serialization/> 

                  <Manager z:Ref=i1 />  

                  <Name>Ahmed</Name> 

            </Employee> 

      </Staff> 

</Department> 

In attribute-free (POCO) world: 

you can use a different ctor, taking a boolean flag, to toggle by-val/by-ref serialization. 

      DataContractSerializer(Type type, IEnumerable<Type> knownTypes, int maxItemsInObjectGraph, bool ignoreExtensionDataObject, bool preserveObjectReferences, IDataContractSurrogate dataContractSurrogate)

To enable circular references for operation or service scope, you can use custom behaviors etc. Essentially you need the ability to hook into serializer instantiation process and create the instance using above overload: 

   1. Subclass DataContractSerializerOperationBehavior 

   2. Ovverride CreateSerializer method 

   3. Create a new DCS instance passing true to preserveObjectReferences param. 

class DataContractSerializerOperationBehaviorEx : DataContractSerializerOperationBehavior 

public DataContractSerializerOperationBehaviorEx(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); 

About these ads

8 Responses to “DataContract Serializer and IsReference property”

  1. […] Using POCO without thinking through WCF’s requirements for objects it serializes.  If you have a WCF service which accepts entities as parameters or returns them as results and you use POCO entities (maybe by using the POCO T4 template or maybe just writing them by hand), then you have to think about WCF’s requirements for its objects because your POCO objects can be almost anything and they may well not be setup properly to work with WCF.  The three ways this comes up most commonly for me are if you have dynamic proxies (either just for lazy loading or for full change tracking), if you have inheritance in your model which the DataContract serializer is unaware of, or if you want to return entire graphs of entities which have cycles.  You can read about the first two issues (and how to address them) here: http://blogs.msdn.com/b/adonet/archive/2010/01/05/poco-proxies-part-2-serializing-poco-proxies.aspx the key concepts are the DataContractResolver and KnownTypes.  For the last issue, the answer is in the IsReference=true parameter to the DataContract attribute.  This is a feature that was added in .Net 3.5sp1 which makes the DataContract serializer smarter about graphs of related objects.  You can read more about that here: http://zamd.net/2008/05/20/datacontract-serializer-and-isreference-property/&#160; […]

  2. […] with [DataContract(IsReference=true)]. For some more details, check out this MSDN document and this blog post by Zulfiqar. If our data classes share some base class (which is always a good idea), […]

  3. […] DataContract Serializer and IsReference property May 2008 2 comments 3 […]

  4. […] Using POCO without thinking through WCF’s requirements for objects it serializes.  If you have a WCF service which accepts entities as parameters or returns them as results and you use POCO entities (maybe by using the POCO T4 template or maybe just writing them by hand), then you have to think about WCF’s requirements for its objects because your POCO objects can be almost anything and they may well not be setup properly to work with WCF.  The three ways this comes up most commonly for me are if you have dynamic proxies (either just for lazy loading or for full change tracking), if you have inheritance in your model which the DataContract serializer is unaware of, or if you want to return entire graphs of entities which have cycles.  You can read about the first two issues (and how to address them) here: http://blogs.msdn.com/b/adonet/archive/2010/01/05/poco-proxies-part-2-serializing-poco-proxies.aspx the key concepts are the DataContractResolver and KnownTypes.  For the last issue, the answer is in the IsReference=true parameter to the DataContract attribute.  This is a feature that was added in .Net 3.5sp1 which makes the DataContract serializer smarter about graphs of related objects.  You can read more about that here: http://zamd.net/2008/05/20/datacontract-serializer-and-isreference-property/&#160; […]

  5. implantar crm…

    […]DataContract Serializer and IsReference property « Zulfiqar's weblog[…]…

  6. SKETSA said

    SKETSA…

    […]DataContract Serializer and IsReference property « Zulfiqar's weblog[…]…

  7. […] DataContract Serializer and IsReference property « Zulfiqar's weblog […]

  8. […] earlier in the XML with the same keys. So strange, because the rest of the item was different. At this website I found out that settings the IsReference property on the data contract creates this behavior. So […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: