January 19, 2008

Serializing with WCF

First of all, I want to apologize for the amount of time it has taken me to put up a new blog post. I typically try to write once a week, but my life has been crazy as of late. The outcome of all of the madness is that I will be packing up my family and moving from Portland, Oregon to Denver, Colorado at the end of this month. (My wife has been accepted into a Master’s degree program there!)Because of this, posts for the next couple of months may be sporadic, as well. (I’m looking for work in the Denver area. If anybody has any leads, let me know… )

In celebration of my move, I thought that I would put together an example that demonstrates two different ways that WCF serializes objects. (This posting will simply show different ways the engine can work. I hope to actually wire it into the WCF pipeline in a future post.) Since I spent last night packing all of my books into boxes, I thought that maybe I’d find a way to organize them. The goal of this application is to create a manifest list allows me quickly find any book that I’ve packed away in a moving box.

As always, the complete code is at the end of the blog post. The amount of code with this post is a little bit greater than usual. I’ll try and write code directly related to the topic within the text, but I would recommend copying the code to studio if you want to follow along.

Since my main objective is to manage books and boxes, I’m going to begin by simply creating a very basic object for each. My Book object will contain two different properties (Title and Author), and my Box object will simply contain an Id. Since we will be using the WCF engine to do our serialization we will need to mark the classes and properties we wish to expose to the serialization engine with the [DataContract] and the [DataMember] attributes.

I now need a method to associate books with boxes. In order to keep my Book and Box object as loosely coupled as possible, I will add a Manifest object to the project which will be responsible for maintaining the relationship between the two. The Manifest object will expose three properties. The Books property will contain a collection of all of the Books on my bookshelf. The Boxes property will contain all of the Boxes I’m using to move. The LineItems property will contain information as to which book is in which box.

Since we are using WCF, the serializer will require both a getter and a setter for every object it serializes. I don’t really want to give consumers of my class the ability to blow away my boxes collection, so I have explicitly created a private setter. This allows WCF the access it needs, while denying direct access to other consumers.

LineItems are added to the collection by calling the AddLineItem method. This method adds the book to the book collection, adds a box to the box collection, and creates a relationship between the two by adding a new LineItem to the LineItems collection. It is important to note that the association created in the LineItems collection does not contain new instances of books and boxes, rather it contains a reference to the master copy in the Books and Boxes collection. (In other words, the association is by reference.)

I’ve overridden the ToString on the manifest object to display the complete list of books and boxes that it contains. There is also a CreateShippingManifest method which will be used to create a generic manifest of three books contained in two boxes. (See code below.)

Box 1 A Brief History of Time
Box 1 Guards Guards
Box 2 The Reptile Room

Now, we’ll make an assumption that I made a mistake in entering a box label into the program. Instead of box 1, I meant the Id to be box 100. We’ll make the change to the box id, and the manifest will automatically pick up the results.

Box 100 A Brief History of Time
Box 100 Guards Guards
Box 2 The Reptile Room

Now, let’s add some code to serialize and deserialize our manifest. This is actually a fairly simple process. The first method will serialize the data to disc (as Xml), the second method will read the file and deserialize it into a new object.

Object after deserialization
Box 1 A Brief History of Time
Box 1 Guards Guards
Box 2 The Reptile Room
Box 1 A Brief History of Time
Box 1 Guards Guards
Box 2 The Reptile Room

But wait a minute! What happened here? We clearly changed the Id of the first box to be 100, yet the results still state that our box is Box 1! The code looks very similar to what we used in the non-serialized objects. The reason that this happens is because of the way the default instance of the DataContractSerializer writes out the results. If you take a look at the Xml file that is created from our serializer, you’ll see results similar to the following:

The Xml does not contain the associations between the manifest items and the books and boxes that we set up so carefully in our code. Changing the box Id in the master collection no longer changes the box id in all of the children.

We can very easily fix this by updating the DataContractSerializer instantiation in our Serialize method. The important parameters in the new constructor are the third and fifth. The third parameter (maxItemsInObjectGraph) indicates the total number of objects that the Xml can contain. If the number of objects in the Xml is exceeded, an error will be raised. The fifth parameter (preserveObjectReferences) indicates that the associations between objects should be preserved.

The important parameters in the new constructor are the third and fifth. The third parameter (maxItemsInObjectGraph) indicates the total number of objects that the Xml can contain. If the number of objects in the Xml is exceeded, an error will be raised. The fifth parameter (preserveObjectReferences) indicates that the associations between objects should be preserved. Now, when we rerun our demo, we see that the Id of the box is correctly updated.

Object after deserialization
Box 1 A Brief History of Time
Box 1 Guards Guards
Box 2 The Reptile Room
Box 100 A Brief History of Time
Box 100 Guards Guards
Box 2 The Reptile Room

Examining the Xml now shows the Id and IdRef structures in place to reassociate the data.

So, there you have it. With WCF you can serialize by value or by reference. Pretty neat stuff. This has kind of been a marathon post, I hope you could follow along. Let me know if you find anything that isn’t clear!
Code Safe!
MW