Requirements of and pitfalls in Windows Phone 7 serialization Unlocking a Windows Phone 7 device for development

Attribute-based transient Tombstoning

Published on Saturday, November 27, 2010 10:48:00 AM UTC in Programming

Hopefully every Windows Phone 7 developer has heard about Tombstoning by now. To avoid writing the same code to persist and retrieve data again and again, there have been quite some efforts in the past to create generic methods of handling this process of application deactivation/termination by various people. James Ashley for example uses the ApplicationSettings and State classes for this (improved by rschiefer here). I personally like implementations that use the data contract serializer (here is an example by Joost van Schaik), because I can selectively choose what values should be serialized by applying the data member attribute only to those properties I want to be persisted.

Lately, I found myself confronted with an additional requirement more than once: I wanted to have my application persist some values only to the transient application state, but not permanently to isolated storage. The scenario for that is that an application might have temporary selections made by the user that should be preserved when the user moves away and returns to your application (e.g. when you're using launchers or choosers), but those values should not be restored when the user starts a new instance of your application. In fact, Microsoft's own recommendations for the user experience are (best practices document):

Ensure that when your application is launched by the user from Start or the installed applications list, the user is taken to a consistent, root experience. It should be evident to the user that they are experiencing a new application instance.

And:

When the user launches a new instance of your application, it is acceptable to present the user with some information about a previous instance, for example, a list of recently opened documents, but the user should not feel like they are continuing a previous session.

This means that you should decide carefully about what values should be only stored temporary when your application is deactivated ("session-dependent values"), and which ones you want to persist to isolated storage for the next launch.

Of course this can be done easily with some code, but being used to the attribute-driven approach of the data contract serializing, I decided to try something similar for the transient storage of temporary values. Here is the result.

The attribute

This is the simplest part. I only defined a new attribute I can use to decorate properties to mark them for this selective serialization. At the moment the attribute does not have any properties, but this can easily be extended, for example to influence the generation of the key value that is used for the application state.

/// <summary>
/// Can be used to mark members of a class so they are taken into
/// account by the <see cref="TransientStore"/> serializer class.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
public sealed class TombstoneDataMemberAttribute : Attribute
{        
}

I'll provide a sample of how to use the attribute and related classes in more detail below.

The transient store class

The logic of that store helper class is to use reflection to get those members of a type which are decorated with the TombstoneDataMember attribute and save or retrieve their values to and from the application state. The core logic looks like this:

private static void ActionOnMatchedProperties<T>(T data, Action<PropertyInfo, string> action)
{
    if (data == null)
    {
        return;
    }

    Type type = data.GetType();
    PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);

    foreach (var property in properties)
    {
        MethodInfo getMethod = property.GetGetMethod(false);
        MethodInfo setMethod = property.GetSetMethod(false);

        if (getMethod == null || setMethod == null)
        {
            // there's no point in continuing if we cannot get and set the value
            continue;
        }

        object[] tombstoneDataMemberAttributes = property.GetCustomAttributes(typeof(TombstoneDataMemberAttribute), false);

        if (tombstoneDataMemberAttributes != null && tombstoneDataMemberAttributes.Length > 0)
        {
            // build key
            string key = type.FullName + "." + property.Name;

            action(property, key);
        }
    }
}

Pretty straight-forward for those familiar with reflection. Get the type, then extract its properties (make sure we can both get and set the property values) and check whether they are decorated with the custom attribute. This logic is the same for both persisting and retrieving, which is why I have moved it to a separate method. Depending on the operation, different actions are passed into this method. For the persist part, it looks like this:

/// <summary>
/// Persists properties of the specified data argument to application 
/// state based on the <see cref="TombstoneDataMember"/> attribute.
/// </summary>
/// <typeparam name="T">The type of the data argument.</typeparam>
/// <param name="data">The data object to inspect for decorated properties.</param>
public static void Persist<T>(T data) where T : class
{
    // execute this for each matched property
    ActionOnMatchedProperties(data, (property, key) =>
        {
            // get value and save to state
            object value = property.GetValue(data, null);

            if (PhoneApplicationService.Current.State.ContainsKey(key))
            {
                PhoneApplicationService.Current.State.Remove(key);
            }

            PhoneApplicationService.Current.State.Add(key, value);
        });            
}

For each matched property, the actual value will be stored in the application state. Accordingly, the retrieve part then looks like:

/// <summary>
/// Retrieves property values for the the specified data argument
/// from application state based on the <see cref="TombstoneDataMember"/> attribute.
/// </summary>
/// <typeparam name="T">The type of the data argument.</typeparam>
/// <param name="data">The data object to retrieve property values for.</param>
public static void Retrieve<T>(T data) where T : class
{
    // execute this for each matched property
    ActionOnMatchedProperties(data, (property, key) =>
        {
            // check if state contains a value for this property
            // and apply
            if (PhoneApplicationService.Current.State.ContainsKey(key))
            {
                object value = PhoneApplicationService.Current.State[key];

                property.SetValue(data, value, null);
            }
        });
}

This code simply reads the value back from application state if it's there, and sets the property value.

Usage

With that code, you can now simply decorate those members of your model or view model that should be only persisted into the transient store with the new attribute, and use that technique side-by-side with the attributes for data contract serialization, for example:

[DataMember]
public double Result
{
    get
    {
        return _result;
    }
    set
    {
        if (_result != value)
        {
            _result = value;
            RaisePropertyChanged("Result");
        }
    }
}

[TombstoneDataMember]
public double TemporaryResult
{
    get
    {
        return _temporaryResult;
    }
    set
    {
        if (_temporaryResult != value)
        {
            _temporaryResult = value;
            RaisePropertyChanged("TemporaryResult");
        }
    }
}

In your event handlers for the application's deactivated and activated events, you can then simply use the transient store class to handle those temporary values, like:

TransientStore.Persist(_mainModel);

And:

TransientStore.Retrieve(_mainModel);

Performance and other considerations

It has been argued that there are much faster options to serialize your data than to use the data contract serializer, but as usual more performance often means less comfort. Often these alternatives require more code and have issues with versioning or similar things (although I haven't tried any of the third party libraries suggested by others yet). I have decided that until I run into performance problems, I'll enjoy the comfort of the data contract serializer as a very simple method to persist and load data. If you're interested in the performance of various serialization methods, a good start is this comparison Kevin Marshall has posted a few weeks ago.

The same probably is true for my approach with the TombstoneDataMember attribute. It uses reflection and hence custom code that uses binary serialization and knows the particular objects to serialize will always be faster. However, as a lazy developer I like comfortable solutions where I don't have to write a lot of code until I absolutely need to. Using an attribute and two lines of code to do this serialization is compelling to me. Also I don't believe that performance will be an issue with this solution, because it all happens in memory, and there's much more optimization potential in the persistence of your permanent state values to isolated storage (which you will have to do in addition anyway).

Another thing you may probably run into is that you will have problems when you want to store multiple objects of the same type, as the current key generation does not allow duplicates. You then have to extend the logic to build your keys. But as long as you persist your (singleton or pseudo-singleton) models or view models, this shouldn't be an issue though.

Tags: Silverlight · Windows Phone 7