Share via


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

Comments

  • Anonymous
    June 02, 2011
    The comment has been removed
  • Anonymous
    June 02, 2011
    Hey Bill, The new OData tombstoning does require that any object being serialized be decorated with DataServiceKeyAttribute, which should be done by the Mango version of the tools when the client data classes are generated from the service reference. If this is not happening, you should check on the following:
  1. That the target version of the project is set to 7.1
  2. That you have, in fact, updated the service reference.
  3. That the Reference.datasvcmap file in the service reference, the ClientAssemblyReference parameter is referencing the 7.1 version of the System.Data.Services.Client.dll assembly.
  4. If all else fails, you can always use the DataSvcUtil.exe utility included with the Mango SDK to manually generate the client data classes and add it to your project in place of the service reference. For more on using this utility, see msdn.microsoft.com/.../ee383989.aspx. Cheers! Glenn Gailey
  • Anonymous
    June 03, 2011
    Hi Glenn, Creating client data classes (step 4 above) did the trick. Thnaks for your quick responses and help. bill

  • Anonymous
    December 03, 2011
    This may be a stupid question, but I simply can't find the answer. Can tombstoning be done somehow also on desktop apps? I need a method to create a copy of the entire context and I have no ideea on how to do it.

  • Anonymous
    December 05, 2011
    The DataServiceState class, which serializes DataServiceContext and DataServiceCollection<T>, is only available in the OData client for Windows Phone 7. It was intended to be used to enable tombstoning in Windows Phone, because a Windows Phone app can get shut down at any point by the OS. I've never investigated trying to clone an existing DataServiceContext, but based on how difficult it seemed to be to serialize and deserialize it correctly maintaining the objects, collections, and state it must be difficult. The OData client for WP is a subset of the full .NET client, but I've also never tried to run it in a non-WP application.

  • Anonymous
    March 02, 2012
    I just found the solution to the problem that I commented on a few minutes ago. When I opened up the Reference.cs file and added [DataContract] just before the Entities (DataServiceContext) class, the problem went away.  I verified that neither "Add Service Reference" and SVCUTIL.EXE generate code with the DataContract attribute. All is good now... Thanks