How to: Upgrade Metadata When the Provider Changes the Metadata Schema
This topic describes how to use a managed language to upgrade a metadata storage service store when the provider changes the metadata schema.
This topic assumes a basic familiarity with C# and Microsoft .NET Framework concepts.
The examples in this topic focus on the following Sync Framework classes and interfaces:
Understanding Metadata Upgrade
The metadata storage service stores replica and item metadata in a lightweight database. A provider can define custom fields in the database that may change as a developer releases new versions of a particular provider. Sync Framework provides support for upgrading the metadata store because of provider version changes.
A metadata store upgrade is necessary only if the custom fields used by the provider change. Changes to the format of the item data do not impact the format of the metadata.
When a provider stores metadata for a replica, it sets the provider version that is compatible with the replica metadata by using ProviderVersion. When a provider later opens the metadata store, it can check the provider version that is associated with the replica metadata. When the version of the provider that opens the metadata store is different than the provider version that is stored in the metadata, the provider can upgrade the metadata schema for the replica.
A metadata store is upgraded by using the following steps.
A SqlSyncMetadataStoreSerializer object is used to serialize the replica metadata.
The replica metadata is removed from the metadata store.
Replica metadata in the new format is created in the metadata store.
The previously serialized replica metadata is imported into the new format by using the SqlSyncMetadataStoreSerializer object.
Sync Framework provides a callback mechanism, IProviderUpgradeCallback, to allow the provider to control the upgrade process, including making any necessary changes to the metadata.
For more information, see Upgrading the Metadata Store Version.
Build Requirements
.NET Framework 2.0 or a later version.
Reference to Microsoft.Synchronization.
Reference to Microsoft.Synchronization.MetadataStorage.
Example
The example code in this topic shows how to upgrade a metadata store that adds a custom field during the upgrade. A class that implements IProviderUpgradeCallback is used to set the value of the new field for each item in the metadata store.
Upgrading the Metadata Store
This example upgrades the metadata store when the current provider version of the replica metadata is less than the specified value. As part of the upgrade, an additional custom field is added to the metadata schema for the replica and is specified as an index field.
Be aware that the SqlMetadataStore object must be disposed and reopened so that all references to the ReplicaMetadata object are released. If this is not done, the call to RemoveReplicaMetadata throws ReplicaMetadataInUseException.
public static void UpgradeMetadataStore(SqlMetadataStore store, string storePath,
SortedList<SyncId, Contact> contactList)
{
// Get the provider version and replica ID from the metadata store.
uint providerVersion = store.GetSingleReplicaMetadata().ProviderVersion;
SyncId replicaId = store.GetSingleReplicaMetadata().ReplicaId;
// Check the provider version of the metadata store and upgrade if necessary.
if (providerVersion < (uint)ContactsProviderVersion.ContactsProvider_v2)
{
// Dispose the store to release all references to the replica metadata
// or the call to RemoveReplicaMetadata will throw ReplicaMetadataInUseException.
store.Dispose();
store = null;
// Reopen the store.
store = SqlMetadataStore.OpenStore(storePath);
// Start a transaction.
store.BeginTransaction();
// Serialize the metadata store in canonical format.
string serializedName = "SerializedStoreForUpgrade.dat";
SyncMetadataStoreSerializer mdsSerializer = store.GetMetadataStoreSerializer();
mdsSerializer.SerializeReplicaMetadata(ContactStore.ContactIdFormatGroup, replicaId,
serializedName, CompatibilityLevel.SyncFrameworkVersion1);
// Remove the replica metadata from the store.
store.RemoveReplicaMetadata(ContactStore.ContactIdFormatGroup, replicaId);
// Initialize replica metadata, adding a new index column for Address.
// Create custom fields for First Name, Last Name, Phone Number. These will be used
// as unique index fields for identifying items between the metadata store and the item store.
// Also include a custom field for Address, which will be used as an index.
FieldSchema[] CustomFields =
{
new FieldSchema(FirstNameField, typeof(string), 100),
new FieldSchema(LastNameField, typeof(string), 100),
new FieldSchema(PhoneNumberField, typeof(string), 20),
new FieldSchema(AddressField, typeof(string), 100)
};
// Specify the index fields.
string[] IndexFields = { FirstNameField, LastNameField, PhoneNumberField };
IndexSchema[] Indexes =
{
new IndexSchema(IndexFields, true),
new IndexSchema(AddressField, false)
};
// Create the metadata for the replica in the metadata store.
ReplicaMetadata newRepMeta = store.InitializeReplicaMetadata(
ContactIdFormatGroup, replicaId, CustomFields, Indexes);
// Import the serialized metadata.
ContactsProviderUpgrader upgrader = new ContactsProviderUpgrader(contactList);
mdsSerializer.DeserializeReplicaMetadata(serializedName, (uint)ContactsProviderVersion.ContactsProvider_v2,
upgrader);
// Set the new provider version.
newRepMeta.ProviderVersion = (uint)ContactsProviderVersion.ContactsProvider_v2;
newRepMeta.SaveReplicaMetadata();
// Commit the transaction.
store.CommitTransaction();
}
}
Responding to Upgrade Callbacks
This example defines a class that implements the IProviderUpgradeCallback interface. In the previous example, an instance of this class is specified to the DeserializeReplicaMetadata method. Sync Framework calls the methods in this interface to allow the provider to control the upgrade and make any necessary changes to the metadata. In the OnProviderUpgradeRequired method of this example, the upgrade is canceled if the provider version currently stored in the replica metadata is not the expected value. In the OnItemMetadataDeserialized method of this example, the value of the new address field is set in the metadata for each item.
class ContactsProviderUpgrader : IProviderUpgradeCallback
{
// The contact list is the item store. Save it so new metadata fields can be updated
// during the upgrade.
public ContactsProviderUpgrader(SortedList<SyncId, Contact> contactList)
{
_contactList = contactList;
}
private SortedList<SyncId, Contact> _contactList;
#region IProviderUpgradeCallback Members
public void OnCustomReplicaMetadataDeserialized(byte[] customReplicaMetadata)
{
// This replica doesn't store custom replica metadata, so there's nothing to do!
}
public void OnItemMetadataDeserialized(ItemMetadata itemMetadata, Dictionary<string, SyncFieldValue> extraFields)
{
// The address field is new in the upgrade, so set it now from the contact list.
Contact contact = _contactList[itemMetadata.GlobalId];
itemMetadata.SetCustomField(ContactStore.AddressField, contact.Address);
}
public void OnProviderUpgradeRequired(uint dwCurrentProviderVersionInFile)
{
// This upgrader can only upgrade from provider version 1.
if ((uint)ContactsProviderVersion.ContactsProvider_v1 != dwCurrentProviderVersionInFile)
{
throw new MetadataStoreProviderVersionMismatchException("Can't upgrade the metadata store from the specified version.");
}
}
Next Steps
Next, you might want to learn more about upgrading the metadata store when the version of Sync Framework changes. For more information, see Upgrading the Metadata Store Version.
You might also want to learn more about using the canonical format of the metadata store to enable components of different versions to interoperate without upgrading the metadata store. For more information, see Accessing Metadata from Components with Different Versions.
See Also
Concepts
Programming Common Standard Custom Provider Tasks
Upgrading the Metadata Store Version