如何在提供程序更改元数据架构时升级元数据

本主题介绍如何在提供程序更改元数据架构时使用托管语言升级元数据存储服务存储区。

本主题假定您基本熟悉 C# 和 Microsoft .NET Framework 概念。

本主题中的示例着重介绍以下 Sync Framework 类和接口:

了解元数据升级

元数据存储服务在轻量级数据库中存储副本和项元数据。提供程序可以定义数据库中的自定义字段可随着开发人员发布特定提供程序的新版本而发生变更。Sync Framework 提供因提供程序版本变更而对元数据存储区进行升级的支持。

只有在自定义字段由提供程序变更使用时,元数据存储区升级才是必需的。对项数据的格式的变更不影响元数据的格式。

在提供程序为副本存储元数据时,它通过使用 ProviderVersion 设置与副本元数据兼容的提供程序版本。在提供程序以后打开元数据存储区时,它可以检查与副本元数据相关联的提供程序版本。打开元数据存储区的提供程序版本与元数据中存储的提供程序版本不同时,提供程序可以升级副本的元数据架构。

通过使用以下步骤升级元数据存储区。

  1. 使用 SqlSyncMetadataStoreSerializer 对象来序列化副本元数据。

  2. 将副本元数据从元数据存储区中删除。

  3. 在元数据存储区中创建采用新格式的副本元数据。

  4. 通过使用 SqlSyncMetadataStoreSerializer 对象将以前序列化的副本元数据导入这个新格式。

Sync Framework 提供一个回调机制 IProviderUpgradeCallback,以便允许提供程序控制升级过程,包括对元数据进行任何必要的变更。

有关更多信息,请参见升级元数据存储区版本

生成要求

示例

本主题中的示例代码演示如何升级一个元数据存储区,该元数据存储区在升级过程中将添加一个自定义字段。使用一个实现 IProviderUpgradeCallback 的类来为元数据存储区中的每一项设置新字段的值。

升级元数据存储区

本示例在副本元数据的当前提供程序版本小于指定值时升级元数据存储区。作为升级的一部分,一个附加的自定义字段将添加到副本的元数据架构并且指定为索引字段。

请注意,必须释放并重新打开 SqlMetadataStore 对象,以便释放对 ReplicaMetadata 对象的所有引用。如果没有这样做,则对 RemoveReplicaMetadata 的调用将引发 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();
    }
}

响应升级回调

本示例定义实现 IProviderUpgradeCallback 接口的类。在前面的示例中,对 DeserializeReplicaMetadata 方法指定此类的实例。Sync Framework 调用此接口中的方法,以便允许提供程序控制升级并对元数据进行必要的变更。在此示例的 OnProviderUpgradeRequired 方法中,如果当前在副本元数据中存储的提供程序版本并非预期值,则升级将被取消。在此示例的 OnItemMetadataDeserialized 方法中,将在每一项的元数据中设置新的 address 字段的值。

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.");
        }
    }

后续步骤

接下来,您最好了解有关在 Sync Framework 的版本变更时升级元数据存储区的详细信息。有关更多信息,请参见升级元数据存储区版本

您最好还要了解有关使用元数据存储区的规范格式的更多信息,以便无需升级元数据存储区即可使不同版本的组件能够互操作。有关更多信息,请参见访问来自不同版本的组件的元数据

请参阅

概念

对常见标准自定义提供程序任务进行编程
升级元数据存储区版本