New OData Tombstoning Behavior in Mango

As I mentioned in my previous post OData Updates in Windows Phone “Mango”, new methods have been added to the DataServiceState class that improve performance and functionality when storing client state. You can now serialize nested binding collections as well as any media resource streams that have not yet been sent to the data service. But what does this new behavior look like?

Storing state in the State dictionary of the PhoneApplicationService essentially involves serializing a DataServiceState object, including a typed DataServiceContext object, one or more DataServiceCollection<T> objects, and all the entity data referenced by these objects. To make this behavior more correct, the old SaveState method is replaced with a new static Serialize method. This new method returns, quite simply, a string that is the XML serialized representation of the stored objects. This works much better for storing in the state dictionary because the DataServiceState is able to explicitly serialize everything before it gets stored. Also, nested collections should now work (this was broken in the Windows Phone 7 version).

The following code, based on the quickstart Consuming a Windows Azure Data Service by using the OData Client, has been updated to use the new Mango tombstoning behavior, including the new static Serialize method to store application state on deactivation:

// Code to execute when the application is deactivated (sent to background).
// This code will not execute when the application is closing.
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
if (App.ViewModel.IsDataLoaded)
{
// Store application state in the state dictionary.
PhoneApplicationService.Current.State["ApplicationState"]
= ViewModel.SaveState();
}
}

// Return a collection of key-value pairs to store in the application state.
public Dictionary<string, string> SaveState()
{
if (App.ViewModel.IsDataLoaded)
{
Dictionary<string, string> state
= new Dictionary<string, string>();

        // Create a new dictionary to store binding collections.
var collections = new Dictionary<string, object>();

        // Add the current Titles binding collection.
collections["Titles"] = App.ViewModel.Titles;

        // Store the current context and binding collections
// in the view model state.
state["DataServiceState"] =
DataServiceState.Serialize(_context, collections);

        state["CurrentPage"] = CurrentPage.ToString();
state["TotalCount"] = TotalCount.ToString();

        return state;
}
else
{
return null;
}
}

A new static Deserialize method on DataServiceState takes the stored serialization and returnes a rehydrated DataServiceState instance, so the re-activation code now looks like this:

// Code to execute when the application is activated (brought to foreground).
// This code will not execute when the application is first launched.
private void Application_Activated(object sender, ActivatedEventArgs e)
{
// If data is not still loaded, try to get it from the state store.
if (!ViewModel.IsDataLoaded)
{
if (PhoneApplicationService.Current.State.ContainsKey("ApplicationState"))
{
// Get back the stored dictionary.
Dictionary<string, string> appState =
PhoneApplicationService.Current.State["ApplicationState"]
as Dictionary<string, string>;

            // Use the returned dictionary to restore
// the state of the data service.
App.ViewModel.RestoreState(appState);
}
}
}

// Restores the view model state from the supplied state dictionary.
public void RestoreState(IDictionary<string, string> appState)
{
// Create a dictionary to hold any stored binding collections.
Dictionary<string, object> collections;

    if (appState.ContainsKey("DataServiceState"))
{
// Deserialize the DataServiceState object.
DataServiceState state
= DataServiceState.Deserialize(appState["DataServiceState"]);

        // Restore the context and binding collections.
var context = state.Context as NetflixCatalog;
collections = state.RootCollections;

        // Get the binding collection of Title objects.
DataServiceCollection<Title> titles
= collections["Titles"] as DataServiceCollection<Title>;

        // Initialize the application with stored data.
App.ViewModel.LoadData(context, titles);

        // Restore other view model data.
_currentPage = Int32.Parse(appState["CurrentPage"]);
_totalCount = Int32.Parse(appState["TotalCount"]);
}
}

Note that with multi-tasking and the new fast application switching functionality in Mango, it is possible that your application state will be maintained in a “dormant” state in memory when your app loses focus. This means that even though the Activated event is still raised, the application data is still there and is immediately redisplayed. This is much faster than tombstoning because you don’t have to deserialize and re-bind everything (and the bound images don’t have to be downloaded again—hurray!). For more information, see Execution Model Overview for Windows Phone in the Mango beta documentation.

Cheers,

Glenn Gailey