Windows Phone 7.5 Isolated Storage for Complex Object Graphs

This post is somewhat related to my series WP7 Simplified. I have been writing the series based on an app codenamed LionHeart and just last night I was trying to get the PhoneDataProvider to save three (3) collections of complex objects. Up till now I’ve been regenerating my mock data each run. That’s OK until you want to start persisting data. Now that I’ve disabled the constant regen of data, I’ve uncovered a small mystery that confounded me for a few hours. So in hopes to save you some time here is what I found.

Basic Serialization Overview

Objects stored in IsolatedStorage are serialized. That means objects retrieved from IsolatedStorage are deserialized. IsolatedStorage uses the DataContractSerializer from WCF. I’m not sure if it’s the same, but that doesn’t matter right now. What that means is it relies on an opt-in pattern for objects to be serialized. Objects that are to be serialized but be adorned with the DataContract attribute and all properties that need to be serialized need to be public, have a getter and a setter, and be adorned with the DataMember attribute. That’s a lot of annoying attribute maintenance if you ask me. Luckily, Windows Phone 7 automatically assumes that each class is adorned with DataContract and all public properties with a getter and a setter are adorned with DataMember so you don’t have to messy up your class with the actual attributes. Cool! Thanks, Windows Phone! However not everything is free. Each each type that will be stored in IsolatedStorage MUST have a public paramerterless (default) constructor. If inheritance is used and there are properties of the base type where derived types might be stored then the KnownType attribute MUST be applied to the base type referenced for all derived types.

The Problem and Fix

Now into the problem I ran into. If you are missing KnownType attributes when calling IsolatedStorageSettings.ApplicationSettings.Save() a SerializationException will be thrown. This is actually good because it tells you what to fix. However, if one of the types is missing a parameterless constructor the Save method will not throw any exceptions. Why is this? Because serialization does not require the parameterless constructor, but deserialization does. So, one might expect an exception when deserializing that object from IsolatedStorage. Deserialization does fail but only throws a KeyNotFoundException. That is absolutely NOT helpful. So I scoured the internet and came across this post discussing manual serializing and deserializing. Figuring it was something to do with my objects failing to serialize or deserialize I used the two methods from the sample for Serialize and Deserialize on each collection of objects that I was about to save to IsolatedStorage. Sure enough I found that I was missing a parameterless constructor and deserialize was failing. Once I fixed my object everything saved just fine.

Serialize, Deserialize, and TestSerialization methods:

public class PhoneDataProvider : IDataProvider
{
	private static string Serialize(object objectToSerialize)
	{
		using (MemoryStream ms = new MemoryStream())
		{
			DataContractSerializer serializer = new DataContractSerializer(objectToSerialize.GetType());
			serializer.WriteObject(ms, objectToSerialize);
			ms.Position = 0;
	
			using (StreamReader reader = new StreamReader(ms))
			{
				return reader.ReadToEnd();
			}
		}
	}
	
	private static T Deserialize<T>(string jsonString)
	{
		using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(jsonString)))
		{
			DataContractSerializer serializer = new DataContractSerializer(typeof(T));
			return (T)serializer.ReadObject(ms);
		}
	}
	
	private void TestSerialization<T>(T originalObject)
	{
		string serializedObject = Serialize(originalObject);
		T deserializedObject = Deserialize<T>(serializedObject);
	
		if (Equals(deserializedObject, default(T)))
		{
			throw new Exception();
		}
	}
}

Using TestSerialization:

public class PhoneDataProvider : IDataProvider
{
	public void SaveChanges()
	{
			#if DEBUG
			
			TestSerialization(_clients);
			TestSerialization(_sessionNotes);
			TestSerialization(_sessions);
			
			#endif
			
			SaveValue(CLIENTS_KEY, _clients);
			SaveValue(SESSION_NOTES_KEY, _sessionNotes);
			SaveValue(SESSIONS_KEY, _sessions);
	}
}

So now, only in DEBUG mode, each complex object graph is “validated” and then saved to IsolatedStorage. I love it when things finally work!

Leave a Reply

Your email address will not be published. Required fields are marked *