How to persist the state of an OData client for Windows Phone 8

[ This article is for Windows Phone 8 developers. If you’re developing for Windows 10, see the latest documentation. ]

Persisting state in an application that uses the Open Data Protocol (OData) client library for Windows Phone requires that you use an instance of the DataServiceState class. The OData client for Wndows Phone is not part of the Windows Phone SDK, you must download it separately. For more information, see WCF Data Services Tools for Visual Studio.

Tip

Attempting to store tracked entity objects directly in a state dictionary can result in errors during serialization. If you need to maintain the state of individual tracked objects, instead store the URI of the entity. You can get the URI of an entity object by calling the TryGetUri(Object, Uri%) method. Later you can retrieve the object from the restored DataServiceContext by passing the stored URI to the TryGetEntity``1(Uri, UMP%) method.

The examples in this topic show how to persist data service state for a single page in the page’s state dictionary when the OnNavigatingFrom(NavigatingCancelEventArgs) method is called, and then restore this data when OnNavigatedTo(NavigationEventArgs) is called. This demonstrates the basics of how to use the DataServiceState class to serialize objects to persist state and how to restore them. However, we recommend employing a Model-View-ViewModel (MVVM) design pattern for your multi-page data applications. When using MVVM, the data is stored in the application’s State dictionary in the Deactivated event handler method and is restored in the Activated event handler. In this case, the activation and deactivation should be performed by the ViewModel. For more information, see Walkthrough: Consuming OData with MVVM for Windows Phone 8.

The examples in this topic extend the single-page application featured in How to consume an OData service for Windows Phone 8. This application uses the Northwind sample data service that is published on the OData website. This sample data service is read-only; attempting to save changes will return an error.

Example

The following example shows how to serialize data service state and store it in the state dictionary for the page. This operation is performed in the OnNavigatingFrom(NavigatingCancelEventArgs) method for the page.

protected override void OnNavigatingFrom(System.Windows.Navigation.NavigatingCancelEventArgs e)
{
    // Define a dictionary to hold an existing 
    // DataServiceCollection<Customer>. 
    var collections = new Dictionary<string, object>();
    collections.Add("Customers", customers);

    // Serialize the data service data and store it in the page state.      
    this.State["DataServiceState"] =
        DataServiceState.Serialize(context, collections);
}

The following example shows how to restore data service state from the page’s state dictionary. This restore is performed from the OnNavigatedTo(NavigationEventArgs) method for the page.

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
    // Get the object data if it is not still loaded.
    if (!this.isDataLoaded)
    {
        object storedState;
        DataServiceState state;

        // Try to get the serialized data service state.
        if (this.State.TryGetValue("DataServiceState", out storedState))
        {
            // Deserialize the DataServiceState object.
            state = DataServiceState.Deserialize(storedState as string);

            // Set the context from the stored object.
            var restoredContext = (NorthwindEntities)state.Context;

            // Set the binding collections from the stored collection.
            var restoredCustomers = state.RootCollections["Customers"]
                as DataServiceCollection<Customer>;

            // Use the returned data to load the page UI.
            this.LoadData(restoredContext, restoredCustomers);
        }
        else
        {
            // If we don't have any data stored, 
            // we need to reload it from the data service.
            this.LoadData();
        }
    }
}

Note that in this example, we must first check the isDataLoaded variable to determine whether the data is still active in memory, which can occur when the application is dormant but not terminated. If the data is not in memory, we first try to restore it from the state dictionary and call the version of the LoadData method that uses the stored data to bind to the UI. If the data is not stored in the state dictionary, we call the version of the LoadData method that gets the data from the data service; this is done the first time that the application is executed.

This example shows the two LoadData method overloads that are called from the previous example.

// Display data from the stored data context and binding collection. 
public void LoadData(NorthwindEntities context, DataServiceCollection<Customer> customers)
{
    this.context = context;
    this.customers = customers;

    this.isDataLoaded = true;
}
private void LoadData()
{
    // Initialize the context and the binding collection. 
    context = new NorthwindEntities(northwindUri);
    customers = new DataServiceCollection<Customer>(context);

    // Define a LINQ query that returns all customers.
    var query = from cust in context.Customers
                select cust;

    // Register for the LoadCompleted event.
    customers.LoadCompleted
        += new EventHandler<LoadCompletedEventArgs>(customers_LoadCompleted);

    // Load the Customers feed by executing the LINQ query.
    customers.LoadAsync(context.Customers);

}
void customers_LoadCompleted(object sender, LoadCompletedEventArgs e)
{
    if (e.Error == null)
    {
        // Handle a paged data feed.
        if (customers.Continuation != null)
        {
            // Automatically load the next page.
            customers.LoadNextPartialSetAsync();
        }
        else
        {
            // Set the data context of the list box control to the sample data.
            this.LayoutRoot.DataContext = customers;

            this.isDataLoaded = true;
        }
    }
    else
    {
        MessageBox.Show(string.Format("An error has occurred: {0}", e.Error.Message));
    }
}

See Also

Other Resources

OData client for Windows Phone 8