Local database migration overview for Windows Phone
February 03, 2014
Applies to: Windows Phone 8 | Windows Phone OS 7.1
Starting with Windows Phone OS 7.1, you can store relational data in a local database that resides in your app’s local folder. As your app evolves, you may need to make changes to the schema of your database after the app has been deployed to a device. If those changes are more extensive than adding columns/indexes/tables, you will need to create a new database and migrate data from the old database to the new database. This topic provides an overview of local database migration. For more information about using a local database in your Windows Phone app, see Local database for Windows Phone.
An alternative to migration is a database update. In an update, you can perform additive operations, such as adding columns, indexes, and tables. For more information, see Walkthrough: Updating a local database app for Windows Phone.
This topic contains the following sections.
In some cases, you may not need to perform a data migration. Windows Phone SDK 7.1 provides APIs for upgrading a local database via additive operations. Using the DatabaseSchemaUpdater class, you use the following methods to update an existing database that has been deployed to the device:
These methods are intended to cover a majority of database schema changes. Because they are strictly additive, changes from these methods are less likely to negatively impact user data.
Rather than perform a database migration, we recommend performing a database update with the DatabaseSchemaUpdater class whenever possible.
The first step in performing a database migration is determining whether one is required. To do this, the app needs to determine if there is a version difference between the database schema defined in its object model and the database schema currently used on the device.
The object model version must be stored somewhere within the app package (either in code or a resource file). The current database version is maintained by the platform and is available from the DatabaseSchemaVersion property of the DatabaseSchemaUpdater class. On startup, the app should check for a mismatch between these values. If the code version is greater than the device version, a migration is needed.
All migration logic should be isolated and encapsulated in one or more migration classes, which are used only to perform the migration. This logic should not be mixed with any part of the app’s object model. If there is a potential that the app will need to handle multiple migration paths, then it should provide a factory class that can return the appropriate migration object. For example, the factory class may have a return object for migrating from version 1.0 to version 1.2 and another object for migrating from version 1.1 to version 1.2. The code devoted to handling migrations will grow in size and complexity in proportion to the number of supported migration paths; consider future app maintenance when making schema changes.
We recommend that the migration code manage both the migration itself and the necessary progress information to bind to the UI. Base progress information on steps (step 2 of 5) or percent complete (73% complete).
Avoid performing migration in the OnNavigatedTo event. Lengthy operations in this method can cause the app to be terminated by the operating system. For more information, see Minimize the Code in Constructors and Loaded Events in App performance considerations for Windows Phone.
If you foresee that your app will need several migrations, you may want to create a base class that can be inherited and extended for each migration path. For example, the base class may handle starting a migration and maintaining progress for it.
In cases where there are tables that are not accessed often, your app may choose to perform the migration on-demand. Rather than requiring the end user to wait for a full migration, the database migration is broken-up into smaller operations, as the user gets into those areas of the app. For example, you may want to separate migration operations for the full version of the app from operations related to the trial version. This approach would help optimize the migration experience for trial users.
In this release, it is not possible to remove columns or tables from an existing database. Even if an object property is no longer relevant for your app, the associated column will remain in the database indefinitely after migration. If that unused column was not defined as nullable in the original database definition (via the CanBeNull column attribute), then a non-null value for that column must be specified with any subsequent inserts for that row.
If your app will replace one table with another, and is no longer using the old table, consider removing all of the rows from the old table to limit the amount of space used to store the unused data.
A local database resides in the local folder. Similar to other files in the local folder, you can delete it using the IsolatedStorageFile class. If the old database is no longer needed after migration, we recommend that your app delete it.
When incorporating migration into your app, consider the following best practices:
Segregate the classes associated with the migration and the old data context into their own namespace. For example, the namespace MyProject.v1DataModel indicates that that code in the namespace is related to the first version of the data model.
In the migration class, create a new database based on the new version of the data model.
After the new database has been created, transfer data on a table-by-table basis. For tables linked by foreign key relationships, copy the parent table first, followed by the child tables.
Maintain the state of the migration in the migration class. For example, update the migration state as each table finishes copying. If the app is terminated before the migration is complete, use the migration state to resume migration in an efficient manner.
When copying large tables with paging, maintain the state of the paging so that it can be resumed on reactivation.