Serializing Cyclic Graphs of objects with WCF

*** This is a repost of a previous post because of moving to a new blog engine in which some formatting was lost ***

Abstract

In this blog post I will be discussing how the serialize cyclic graphs of objects using WCF, including data contracts and POCO’s ( Plain Old CLR Objects).

Introduction

WCF allows you to serialize any number of objects over the network or to disk using the DataContractSerializer class. However, some issues can arise when you have cycles in your object graph, and especially when you want some shared objects to stay shared!

Serializing cyclic objects

Let’s look at this with a couple of examples. First I want some datacontracts with possible cycles:

  [DataContract(Namespace=Namespaces.U2U, Name="address", IsReference=false)]
  public class Address
  {
    [DataMember(Name="street", Order=1)]
    public string Street { get; set; }
    [DataMember(Name="city", Order=2)]
    public string City { get; set; }
  }

  [DataContract(Namespace = Namespaces.U2U, Name = "person", IsReference = false)]
  public class Person
  {
    [DataMember(Name="name", Order=1)]
    public string Name { get; set; }
    [DataMember(Name="age", Order=2)]
    public int Age { get; set; }
    [DataMember(Name="partner",Order=3)]
    public Person Partner { get; set; }
    [DataMember(Name = "address", Order = 4)]
    public Address Address { get; set; }
  }

 

I’ll create a person object with a reference to another person. Since they are partners, they will point to each other.

image

DataContractSerializer ser = new DataContractSerializer(typeof(Person));
string path = Path.Combine( Environment.CurrentDirectory, "cartoon.xml" );
using (FileStream ms = File.Open(path, FileMode.Create, FileAccess.Write))
{
  Address home = new Address { Street = "ToonLane", City = "ToonTown" };
  Person tom = new Person { Name = "Tom", Age = 4, Address = home };
  Person jerry = new Person { Name = "Jerry", Age = 2, Address = home };
  tom.Partner = jerry;
  jerry.Partner = tom;
  ser.WriteObject(ms, tom);
}

So what happens when you try this? You’ll get an exception:

image

[NOTE: By the way, this is much better then in .NET 1.0 with the XmlSerializer, it would serialize the first object, then follow the link to the second, then back the to first, then back to the second, until your disk ran out of space :) Now XmlSerializer will also throw an exception if you try to serialize cycles.]

Using IsReference

To make the exception go away, you add IsReference=true to the Person’s DataContract:

The result now looks like this:

<person z:Id="i1" 
xmlns="urn://www.u2u.be/samples/wcf/2009"
xmlns:i=http://www.w3.org/2001/XMLSchema-instance
xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"> <name>Tom</name> <age>4</age> <partner z:Id="i2"> <name>Jerry</name> <age>2</age> <partner z:Ref="i1"/> <address> <street>ToonLane</street> <city>ToonTown</city> </address> </partner> <address> <street>ToonLane</street> <city>ToonTown</city> </address> </person>

Look how Jerry refers back to Tom using the z:Ref! Now you can easily send objects with cycles. But wait! Both point to the same address, but now Tom and Jerry both have an address, although identical.

image

Again we can change this by changing the IsReference to true in the Address DataContract. Now the result looks like this:

<person z:Id="i1" xmlns="urn://www.u2u.be/samples/wcf/2009" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
  <name>Tom</name>
  <age>4</age>
  <partner z:Id="i2">
    <name>Jerry</name>
    <age>2</age>
    <partner z:Ref="i1"/>
    <address z:Id="i3">
      <street>ToonLane</street>
      <city>ToonTown</city>
    </address>
  </partner>
  <address z:Ref="i3"/>
</person>

Please note that now both Tom and Jerry use the same address instance, not clones.

Serializing cyclic POCO’s with WCF

“And what about POCO’s? (Plain Old Clr Objects) I hear you say. Well in that case you cannot attach the DataContract attribute of course, so you‘ll need to use another variant of the DataContractSerializer constructor:

new DataContractSerializer(typeof(PocoPerson), null, int.MaxValue, false
                          , /* preserveObjectReferences */ true, null, null);

So, using this constructor the previous example looks like this:

  public class PocoPerson
  {
    public string Name { get; set; }
    public int Age { get; set; }
    public PocoPerson Partner { get; set; }
    public PocoAddress Address { get; set; }
  }

  public class PocoAddress
  {
    public string Street { get; set; }
    public string City { get; set; }
  }
  DataContractSerializer ser = 
    new DataContractSerializer(typeof(PocoPerson), null, int.MaxValue, false
                              , /* preserveObjectReferences */ true, null, null);
  string path = Path.Combine(Environment.CurrentDirectory, "cartoon2.xml");
  using (FileStream ms = File.Open(path, FileMode.Create, FileAccess.Write))
  {
    PocoAddress home = new PocoAddress { Street = "ToonLane", City = "ToonTown" };
    PocoPerson tom = new PocoPerson { Name = "Tom", Age = 4, Address = home };
    PocoPerson jerry = new PocoPerson { Name = "Jerry", Age = 2, Address = home };
    tom.Partner = jerry;
    jerry.Partner = tom;
    ser.WriteObject(ms, tom);
}

Running this will result in following Xml:

<PocoPerson z:Id="1" 
            xmlns="http://schemas.datacontract.org/2004/07/ReferenceSerialization" 
            xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 
            xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
  <Address z:Id="2">
    <City z:Id="3">ToonTown</City>
    <Street z:Id="4">ToonLane</Street>
  </Address>
  <Age>4</Age>
  <Name z:Id="5">Tom</Name>
  <Partner z:Id="6">
    <Address z:Ref="2" i:nil="true"/>
    <Age>2</Age>
    <Name z:Id="7">Jerry</Name>
    <Partner z:Ref="1" i:nil="true"/>
  </Partner>
</PocoPerson>

Using a custom behavior

Of course it is not that easy to use this contructor inside a real service, because you’re not in charge of creating the serializer. But hey, WCF is very extensible right? So to plug the hole, you need to build your own Behavior, specifically a DataContractSerializerOperationBehavior.

public class CyclicSerializationBehavior : DataContractSerializerOperationBehavior
{
  public CyclicSerializationBehavior(OperationDescription od)
    : base(od)
  { }

  private const bool preserveObjectReferences = true;

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

To get this behavior installed you need to write some code to add it the the operation’s behavior, or even better is to use an IContractBehavior:

  [AttributeUsage(AttributeTargets.All)]
  public class CyclicSerializationAttribute : Attribute, IContractBehavior
  {
    public void AddBindingParameters(ContractDescription contractDescription, 
ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ContractDescription contractDescription,
ServiceEndpoint endpoint, ClientRuntime clientRuntime) { ReplaceDataContractSerializerOperationBehavior(contractDescription); } public void ApplyDispatchBehavior(ContractDescription contractDescription,
ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime) { ReplaceDataContractSerializerOperationBehavior(contractDescription); } private void ReplaceDataContractSerializerOperationBehavior(ContractDescription contractDescription) { foreach (OperationDescription operation in contractDescription.Operations) { DataContractSerializerOperationBehavior beh =
operation.Behaviors.Find<DataContractSerializerOperationBehavior>(); if (beh != null) { operation.Behaviors.Remove(beh); operation.Behaviors.Add(new CyclicSerializationBehavior(operation)); } } } public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint) { } }

I made this class into an attribute so you can apply it to your service contract or service class, and when WCF loads it it replaces the default DataContractSerialerOperationBehavior. At first I simply added it to the collection of behaviors, but then I got all kinds of strange things :)

So, to use this now you apply the attribute to the service contract:

  [ServiceContract]
  [CyclicSerializationAttribute]
  public interface ICyclicSerializationService
  {
    [OperationContract]
    PocoPerson GetPocoPerson();
  }

Don’t forget, you should add this to both the server en client side.


Comments (11) -

December 18. 2009 04:46 AM

Chocolate Ice Cream Recipes

When I originally commented I clicked the "Notify me when new comments are added" checkbox and now each time a comment is added I get four emails with the same comment.
Is there any way you can remove me from that service?
Thanks!

Chocolate Ice Cream Recipes

December 18. 2009 09:51 AM

Hard Candy Recipes

Do you accept guest posts? I would love to write couple articles here.
I was wondering what is up with that weird gravatar??? I know 5am is early and I'm not looking my best at that hour, but I hope I don't look like this! I might however make that face if I'm asked to do 100 pushups. lol

Hard Candy Recipes

December 19. 2009 07:28 AM

set up a company

Nice to be visiting your blog again, it has been months for me. Well this article that i've been waited for so long. I need this article to complete my assignment in the college, and it has same topic with your article. Thanks, great share.

set up a company

December 22. 2009 10:33 AM

pool solar panels

Excellent read, I just passed this onto a colleague who was doing a little research on that. And he actually bought me lunch because I found it for him smile So let me rephrase that: Thanks for lunch!

pool solar panels

December 22. 2009 11:12 AM

barcode mobile scanner

Me and my friend were arguing about an issue similar to this! Now I know that I was right. lol! Thanks for the information you post.

barcode mobile scanner

January 7. 2010 02:04 PM

Bredb&#229;nd priser

Dude.. I am not much into reading, but somehow I got to read lots of articles on your blog. Its amazing how interesting it is for me to visit you very often.

Bredbånd priser

January 9. 2010 11:42 PM

alen air purifier

This is my first time i visit here. I found so many interesting stuff in your blog especially its discussion. From the tons of comments on your articles, I guess I am not the only one having all the enjoyment here! keep up the good work.

alen air purifier

January 13. 2010 02:38 AM

Valentines Day 2010

Keep 'em coming... you all do such a great job at such Concepts... can't tell you how much I, for one appreciate all you do!

Valentines Day 2010

January 15. 2010 03:48 AM

out of body experience

This is my first time i visit here. I found so many interesting stuff in your blog especially its discussion. From the tons of comments on your articles, I guess I am not the only one having all the enjoyment here! keep up the good work.

out of body experience

January 15. 2010 10:30 AM

Abdominal Exercises

Me and my friend were arguing about an issue similar to this! Now I know that I was right. lol! Thanks for the information you post.

Abdominal Exercises

January 18. 2010 11:43 PM

IT support

The blog was absolutely fantastic! Lots of great information and inspiration, both of which we all need!

IT support

January 25. 2010 02:04 PM

pay day loans

Do not save your loving speeches For your friends till they are dead; Do not write them on their tombstones, Speak them rather now instead.

pay day loans

January 25. 2010 03:58 PM

sugar babys

This is such a great resource that you are providing and you give it away for free. I love seeing websites that understand the value of providing a quality resource for free. It is the old what goes around comes around routine. Did you acquired lots of links and I see lots of trackbacks??

sugar babys

January 31. 2010 02:25 PM

Electric Train Sets

I was very pleased to find this site.I wanted to thank you for this great read!! I definitely enjoying every little bit of it and I have you bookmarked to check out new stuff you post.

Electric Train Sets

February 3. 2010 03:51 PM

cash loans

Work is not man's punishment. It is his reward and his strength and his pleasure.

cash loans

February 3. 2010 03:51 PM

payday loans

Believe in yourself, never give up and go about your business with passion drive and enthusiasm.

payday loans

February 4. 2010 11:20 PM

online personal loans

Success usually comes to those who are too busy to be looking for it.

online personal loans

February 4. 2010 11:20 PM

pay day loans

One that would have the fruit must climb the tree.

pay day loans

February 5. 2010 04:22 AM

seo packages

Me and my friend were arguing about an issue similar to this! Now I know that I was right. lol! Thanks for the information you post.

seo packages

February 5. 2010 11:09 AM

seo packages

Me and my friend were arguing about an issue similar to this! Now I know that I was right. lol! Thanks for the information you post.

seo packages

February 8. 2010 07:23 PM

juicer machine

This article gives the light in which we can observe the reality. this is very nice one and gives indepth information. thanks for this nice article

juicer machine

February 9. 2010 09:08 AM

teeth whitening

It is better to err on the side of daring than the side of caution.

teeth whitening

February 9. 2010 09:08 AM

teeth whitening free kit

There is no victory at bargain basement prices.

teeth whitening free kit

February 9. 2010 09:08 AM

teeth whitening free kit

Beauty is truth, and truth is beauty

teeth whitening free kit

February 10. 2010 08:17 AM

indoor gardening

Howdy, i read your blog occasionally and i own a similar one and i was just wondering if you get a lot of spam comments? If so how do you prevent it, any plugin or anything you can advise? I get so much lately it's driving me mad so any assistance is very much appreciated.

indoor gardening

February 13. 2010 07:09 AM

low carb foods diet

Took me time to read all the comments, but I really enjoyed the article. It proved to be Very helpful to me and I am sure to all the commenters here! It's always nice when you can not only be informed, but also entertained! I'm sure you had fun writing this article.

low carb foods diet

February 15. 2010 02:37 AM

nitrogen tank

Just wanted to give you a shout from the valley of the sun, great information. Much appreciated.

nitrogen tank

February 26. 2010 01:47 AM

Wealthy Affiliate Scam

Couldn?t be written any better. Reading this post reminds me of my old room mate! He always kept talking about this. I will forward this article to him. Pretty sure he will have a good read. Thanks for sharing!

Wealthy Affiliate Scam

February 28. 2010 05:29 AM

aion guide

Just wanted to give you a shout from the valley of the sun, great information. Much appreciated.

aion guide

February 28. 2010 09:31 PM

SEO Package

Pretty good post. I just stumbled upon your blog and wanted to say that I have really enjoyed reading your blog posts. Any way I'll be subscribing to your feed and I hope you post again soon.

SEO Package

February 28. 2010 10:12 PM

umbrella payroll

Pretty good post. I just stumbled upon your blog and wanted to say that I have really enjoyed reading your blog posts. Any way I'll be subscribing to your feed and I hope you post again soon.

umbrella payroll

March 9. 2010 05:20 AM

Calvin Guevarra

Great site, where did you come up with the information in this posting? I'm pleased I found it though, ill be checking back soon to see what other articles you have.

Calvin Guevarra

March 15. 2010 09:46 PM

Jacques Raglin

This is a very intriguing post, I was looking for this info. Just so you know I located your blog when I was researching for blogs like mine, so please check out my site sometime and leave me a comment to let me know what you think.

Jacques Raglin

October 21. 2011 07:51 AM

how to get a six pack fast

Thank you my friend for taking the time here to really show me the ropes in terms of getting ahead in life and really helping me be the best person that I can possibly be.

how to get a six pack fast

October 21. 2011 07:53 AM

jonny rocxker

Thank you my friend for <a href="howtogetasixpackfasthq.org/.../how-to-get-a-six-pack-fast">how to get a six pack fast</a> taking the time here to really show me the ropes in terms of getting ahead in life and really helping me be the best person that I can possibly be.

jonny rocxker

October 21. 2011 07:53 AM

jonny rocxkerer

Thank you my friend for taking the time here to really show me the ropes in terms of getting ahead in life and really helping me be the best person that I can possibly be. - howtogetasixpackfasthq.org/.../how-to-get-a-six-pack-fast

jonny rocxkerer

November 2. 2011 03:01 AM

jon rocker

My friend I really believe that the [url=http://howtoloselegfatnow.com/]how to lose leg fat[/url] world is yours if you work really hard and if you just work at achieving and focusing on exactly what it is that you want. So thank you very much.

jon rocker

January 16. 2012 06:07 PM

solar power systems


Informative and interesting post……..
www.greenbiz.com.pk/solar_pv_panel_pk_res.php

solar power systems

February 2. 2012 06:46 PM

Wind Turbines

you've got some great techniques thanks for sharing !!
http://www.greenbiz.com.pk/wind-energy.php

Wind Turbines

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading

Download the U2U brochure

Download Brochure

Receive the U2U Newsletter. Submit your email address:
 
 


 


Search

rss  RSS

Recent posts

None

Archive