Share via


Add offline data sync to your Windows (WPF) app

Note

This product is retired. For a replacement for projects using .NET 8 or later, see the Community Toolkit Datasync library.

This tutorial covers the offline sync feature of Azure Mobile Apps for the WPF quickstart app. Offline sync allows end users to interact with a mobile app—viewing, adding, or modifying data—even when there's no network connection. Changes are stored in a local database. Once the device is back online, these changes are synced with the remote backend.

Before starting this tutorial, you should have completed the WPF Quickstart Tutorial, which includes creating a suitable backend service. We also assume you have added authentication to your application. You can add offline capabilities to your app without authentication.

Update the app to support offline sync

In online operation, you read to and write from a IRemoteTable<T>. When using offline sync, you read to and write from a IOfflineTable<T> instead. The IOfflineTable<T> is backed by an on-device SQLite database, and synchronized with the backend database.

Add the necessary NuGet packages

In Visual Studio:

  1. Right-click on the TodoApp solution, then select Manage NuGet Packages for Solution....

  2. In the new tab, select Browse, then enter Microsoft.Datasync.Client in the search box.

    Screenshot of adding the offline NuGet in Visual Studio.

  3. Select the Microsoft.Datasync.Client.SQLiteStore package.

  4. In the right-hand pane, select all the client projects (except the TodoAppService.NET6 project).

  5. Select Install.

  6. Accept the license agreement when prompted.

Update the remote service client

Open the TodoApp.Data project and locate the RemoteTodoService.cs class (within the Services directory). Update the class as follows:

  1. Add the following using statement to the top of the file:

    using Microsoft.Datasync.Client.SQLiteStore;
    
  2. Change the definition of _table to be an IOfflineTable<TodoItem>:

    /// <summary>
    /// Reference to the table used for datasync operations.
    /// </summary>
    private IOfflineTable<TodoItem> _table = null;
    
  3. Add a new property for storing the offline database location:

    /// <summary>
    /// The path to the offline database
    /// </summary>
    public string OfflineDb { get; set; }
    
  4. Update the InitializeAsync method to define the offline database:

    // Create the offline store definition
    var connectionString = new UriBuilder { Scheme = "file", Path = OfflineDb, Query = "?mode=rwc" }.Uri.ToString();
    var store = new OfflineSQLiteStore(connectionString);
    store.DefineTable<TodoItem>();
    var options = new DatasyncClientOptions
    {
        OfflineStore = store,
        HttpPipeline = new HttpMessageHandler[] { new LoggingHandler() }
    };
    
    // Create the datasync client.
    _client = TokenRequestor == null 
        ? new DatasyncClient(Constants.ServiceUri, options)
        : new DatasyncClient(Constants.ServiceUri, new GenericAuthenticationProvider(TokenRequestor), options);
    
    // Initialize the database
    await _client.InitializeOfflineStoreAsync();
    
    // Get a reference to the offline table.
    _table = _client.GetOfflineTable<TodoItem>();
    
    // Set _initialized to true to prevent duplication of locking.
    _initialized = true;
    
  5. Update the RefreshItemsAsync() to do offline synchronization:

    /// <summary>
    /// Refreshes the TodoItems list manually.
    /// </summary>
    /// <returns>A task that completes when the refresh is done.</returns>
    public async Task RefreshItemsAsync()
    {
        await InitializeAsync();
    
        // First, push all the items in the table.
        await _table.PushItemsAsync();
    
        // Then, pull all the items in the table.
        await _table.PullItemsAsync();
    
        return;
    }
    

Set the offline database location

In the TodoApp.WPF project, edit the App.xaml.cs file. Change the definition of the RemoteTodoService as follows:

TodoService = new RemoteTodoService(async () => await GetAuthenticationToken())
{
    OfflineDb = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\offline.db"
};

If you have not completed the authentication tutorial, the definition should look like this instead:

TodoService = new RemoteTodoService()
{
    OfflineDb = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\offline.db"
};

Note

You can store the offline database wherever you have read/write/create permissions on a Windows system. The Environment.SpecialFolder class gives standard locations according to the application.

Test the app

The app doesn't synchronize with the backend until the refresh icon is pressed. To test:

  1. Open the Azure portal.

  2. Open the resource group that contains the resources for the quickstart.

  3. Select the quickstart database.

  4. Select the Query editor (preview).

  5. Log in with SQL server authentication using the same credentials you set up for the database.

    • If necessary, you'll be prompted to allow access for your IP address. Select the link to update the allowlist, then press OK to retry the login.
  6. In the query editor, enter SELECT * FROM [dbo].[TodoItems]. Then select Run.

A list of the current TodoItems will be displayed.

Screenshot of the results in the S Q L query editor.

Now, make some changes through your app. DO NOT PRESS REFRESH (yet).

Repeat the SQL statement in the Azure portal and verify that no changes have been made to the data in the database.

Select the Refresh icon on your app to push the data in queue to the backend service. You'll see the HTTP transactions happening in the Output Debug window.

Repeat the SQL statement in the Azure portal and verify that your changes have been pushed to the remote service.

Clean up resources

Unless you're doing another quick start tutorial, you can delete the resources associated with the backend service now.

  1. Open the Azure portal.
  2. Select the resource group holding the quick start resources.
  3. Select Delete resource group.
  4. Follow the instructions to confirm the deletion.

You can also use the Azure CLI:

az group delete -g quickstart

If you used the Azure Developer CLI to deploy resources, you can use the azd down command instead.

The deletion will take a few minutes to complete.

Next steps