HOW TO:建立 Managed 同步處理提供者
本主題描述如何使用 Managed 語言來建立 Sync Framework 同步處理提供者,以便同步處理自訂資料存放區中的資料。
本主題假設您對於 C# 和 Microsoft .NET Framework 概念有基本的了解。
本主題的範例將重點放在下列 Sync Framework 類別和介面:
了解同步處理提供者
同步處理提供者是在執行同步處理期間代表複寫的軟體元件。這個元件可以讓複寫與其他複寫同步處理其資料。為了讓同步處理開始,應用程式會先建立 SyncOrchestrator 物件,並將它連接到兩個 SyncProvider 物件,然後啟動同步處理。其中一個提供者代表來源複寫。來源複寫會透過其 GetChangeBatch 方法,為已變更的項目提供中繼資料,並透過 IChangeDataRetriever 物件提供項目資料。另一個提供者代表目的地複寫。目的地複寫透過其 ProcessChangeBatch 方法,接收已變更項目的中繼資料,然後使用 Sync Framework 提供的 NotifyingChangeApplier 物件連同其本身的 INotifyingChangeApplierTarget 物件,將此變更套用至其項目存放區。
如需同步處理提供者所扮演之角色的詳細資訊,請參閱實作標準的自訂提供者。
組建需求
.NET Framework 2.0 或更新版本。
範例
本主題中的範例程式碼示範如何實作基本類別和介面方法,這些類別和方法是複寫參與 Sync Framework 同步處理社群所需的項目 (不論做為來源或目的地)。這個範例中的複寫是一個文字檔,這個檔案會將連絡人資訊儲存為以逗號分隔的值清單。要同步處理的項目是這個檔案中所包含的連絡人。此範例也使用透過中繼資料儲存服務 API 實作的自訂中繼資料存放區。如需中繼資料儲存服務的詳細資訊,請參閱 Sync Framework Metadata Storage Service。
在下列範例中,_itemStore
代表一個物件,此物件包含了項目存放區和中繼資料存放區。
實作 SyncProvider 和 KnowledgeSyncProvider
提供者的進入點是 SyncProvider 類別。此類別是用來做為其他功能更強大之類別的基底類別。此範例使用 KnowledgeSyncProvider 類別。
宣告 KnowledgeSyncProvider
將 KnowledgeSyncProvider 加入至類別繼承清單。
class ContactsProviderXmlMetadataNoChangeUnits : KnowledgeSyncProvider
將 SyncProvider 和 KnowledgeSyncProvider 方法加入至類別。
IdFormats 屬性
在 SyncOrchestrator 物件上呼叫 Synchronize 時,Sync Framework 會在來源和目的地提供者上呼叫 IdFormats。IdFormats 屬性會傳回提供者所使用的識別碼格式結構描述。此結構描述在兩個提供者上必須相同。這個範例會定義包含 SyncGlobalId 的項目識別碼大小、包含複寫之絕對路徑的複寫識別碼,以及列舉成員的變更單位識別碼。
public override SyncIdFormatGroup IdFormats
{
get
{
SyncIdFormatGroup FormatGroup = new SyncIdFormatGroup();
// Item IDs are of SyncGlobalId type, so they are fixed length and contain a ulong prefix plus a Guid.
FormatGroup.ItemIdFormat.IsVariableLength = false;
FormatGroup.ItemIdFormat.Length = (ushort)(sizeof(ulong) + Marshal.SizeOf(typeof(Guid)));
// Replica IDs are the absolute path to the item store, so they are variable length with maximum
// length equal to the maximum length of a path.
FormatGroup.ReplicaIdFormat.IsVariableLength = true;
FormatGroup.ReplicaIdFormat.Length = 260 * sizeof(char);
return FormatGroup;
}
}
BeginSession 方法
Sync Framework 在呼叫其他任何方法之前,會在來源和目的地提供者上呼叫 BeginSession。這個方法會通知提供者,它正在聯結同步處理工作階段,並傳遞包含工作階段狀態資訊的物件給提供者。這個實作會儲存工作階段狀態物件,或是在提供者已經聯結同步處理工作階段時擲回 SyncInvalidOperationException。
public override void BeginSession(SyncProviderPosition position, SyncSessionContext syncSessionContext)
{
// If this object is already in a session, throw an exception.
if (null != _sessionContext)
{
throw new SyncInvalidOperationException();
}
_sessionContext = syncSessionContext;
}
GetSyncBatchParameters 方法
Sync Framework 會在目的地提供者上呼叫 GetSyncBatchParameters。這個方法會擷取來源提供者應該包含於變更批次中的變更數目,並取得目的地提供者的目前知識。此實作會從中繼資料存放區擷取知識,並將批次大小設定為 10
。
public override void GetSyncBatchParameters(out uint batchSize, out SyncKnowledge knowledge)
{
// Set a batch size of 10.
batchSize = 10;
// Return the current knowledge of the replica.
knowledge = _itemStore.ContactReplicaMetadata.GetKnowledge();
}
中繼資料存放區物件會使用 GetKnowledge 來傳回此複寫的目前知識。如果此複寫尚未包含任何知識,這個實作會建立新的 SyncKnowledge 物件,並將知識的滴答計數設定為此複寫目前的滴答計數。
public override SyncKnowledge GetKnowledge()
{
// If the replica does not yet contain any knowledge, create a new knowledge object.
if (null == _knowledge)
{
_knowledge = new SyncKnowledge(IdFormats, ReplicaId, _tickCount);
}
// Ensure the tick count of the knowledge is set to the current tick count of the replica.
_knowledge.SetLocalTickCount(_tickCount);
return _knowledge;
}
GetChangeBatch 方法
Sync Framework 在來源提供者上呼叫 GetChangeBatch 時,同步處理工作階段會盡快啟動。這個方法會擷取要傳送至目的地提供者的變更批次,而且也傳回資料擷取器介面。Sync Framework 會使用資料擷取器介面來取得目的地提供者用來擷取變更之項目資料的 object,這些變更會套用到目的地複寫。Sync Framework 會重複呼叫 GetChangeBatch,直到傳送最後一個批次為止。來源提供者會藉由在變更批次物件上呼叫 SetLastBatch 方法,指出批次是最後一個批次。此實作會將變更列舉工作委派給中繼資料存放區物件。提供者物件會實作 IChangeDataRetriever,因此它會在 changeDataRetriever
參數中傳回。
public override ChangeBatch GetChangeBatch(uint batchSize, SyncKnowledge destinationKnowledge, out object changeDataRetriever)
{
// Return this object as the IChangeDataRetriever object that is called to retrieve item data.
changeDataRetriever = this;
// Call the metadata store to get a batch of changes.
return _itemStore.ContactReplicaMetadata.GetChangeBatch(batchSize, destinationKnowledge);
}
中繼資料存放區物件會使用 GetChangeBatch 來傳回變更的批次。這個實作會將項目當做依項目識別碼排序的 ItemMetadata 物件清單,儲存到中繼資料存放區。隨即列舉項目,而且如果變更未包含在目的地知識中,則會將此變更加入至變更批次物件中的已排序群組。當中繼資料存放區中的所有項目都已經列舉時,便會在變更批次上呼叫 SetLastBatch。
public override ChangeBatch GetChangeBatch(uint batchSize, SyncKnowledge destinationKnowledge)
{
// The destination knowledge must be converted to be compatible with the source replica
// before it can be used.
SyncKnowledge mappedDestKnowledge = _knowledge.MapRemoteKnowledgeToLocal(destinationKnowledge);
// Create a new change batch, initialized by using the current knowledge of the source replica
// and a new ForgottenKnowledge object.
ChangeBatch changeBatch = new ChangeBatch(IdFormats, GetKnowledge(), new ForgottenKnowledge());
// Start a group of changes in the change batch. The group is ordered by item ID.
// _getChangeBatchCurrent is 0 the first time GetChangeBatch is called, and is used to track the
// position in the metadata store for subsequent calls to GetChangeBatch.
changeBatch.BeginOrderedGroup(_items.Values[_getChangeBatchCurrent].GlobalId);
// itemsAdded is incremented each time a change is added to the change batch. When itemsAdded
// is greater than the requested batch size, enumeration stops and the change batch is returned.
int itemsAdded = 0;
ItemMetadata itemMeta;
// Enumerate items and add a change to the change batch if it is not contained in the
// destination knowledge.
// _items is a SortedList that contains ItemMetadata objects that are ordered by item ID.
for (; itemsAdded <= batchSize && _getChangeBatchCurrent < _items.Count; _getChangeBatchCurrent++)
{
itemMeta = _items.Values[_getChangeBatchCurrent];
ChangeKind kind = (itemMeta.IsDeleted) ? ChangeKind.Deleted : ChangeKind.Update;
ItemChange change = new ItemChange(IdFormats, ReplicaId, itemMeta.GlobalId, kind, itemMeta.CreationVersion,
itemMeta.ChangeVersion);
// If the change is not contained in the destination knowledge, add it to the change batch.
if (!mappedDestKnowledge.Contains(change))
{
changeBatch.AddChange(change);
itemsAdded++;
}
}
// End the group of changes in the change batch. Pass the current source knowledge.
changeBatch.EndOrderedGroup(_items.Values[_getChangeBatchCurrent - 1].GlobalId, _knowledge);
// When all items in the metadata store have been enumerated, set this batch as the
// last batch.
if (_getChangeBatchCurrent == _items.Count)
{
changeBatch.SetLastBatch();
}
return changeBatch;
}
ProcessChangeBatch 方法
在 Sync Framework 透過呼叫其 GetChangeBatch 方法,取得來源提供者的變更批次之後,Sync Framework 會在目的地提供者上呼叫 ProcessChangeBatch。此方法會套用變更至目的地複寫。將會針對從來源提供者上之 GetChangeBatch 擷取的每一個變更批次呼叫這個方法一次。此實作會使用中繼資料存放區物件,從來源提供者取得項目的本機版本資訊。然後它會建立 Sync Framework 所實作的 NotifyingChangeApplier 物件,並呼叫它的 ApplyChanges 方法。
public override void ProcessChangeBatch(ConflictResolutionPolicy resolutionPolicy, ChangeBatch sourceChanges, object changeDataRetriever, SyncCallbacks syncCallbacks, SyncSessionStatistics sessionStatistics)
{
// Use the metadata store to get the local versions of changes received from the source provider.
IEnumerable<ItemChange> destVersions = _itemStore.ContactReplicaMetadata.GetLocalVersions(sourceChanges);
// Use a NotifyingChangeApplier object to process the changes. Note that this object is passed as the INotifyingChangeApplierTarget
// object that will be called to apply changes to the item store.
NotifyingChangeApplier changeApplier = new NotifyingChangeApplier(IdFormats);
changeApplier.ApplyChanges(resolutionPolicy, sourceChanges, (IChangeDataRetriever)changeDataRetriever, destVersions,
_itemStore.ContactReplicaMetadata.GetKnowledge(), _itemStore.ContactReplicaMetadata.GetForgottenKnowledge(),
this, _sessionContext, syncCallbacks);
}
中繼資料存放區物件會使用 GetLocalVersions,從來源提供者傳回項目的本機版本資訊。這個實作會列舉變更批次中從來源提供者傳送的變更。如果項目位於目的地中繼資料內,其版本資訊會加入至包含版本資訊的變更清單中。如果目的地中繼資料內沒有此項目存在,就會在本機版本清單中將它標示為不明的項目。
public override IEnumerable<ItemChange> GetLocalVersions(ChangeBatch sourceChanges)
{
List<ItemChange> localVersions = new List<ItemChange>();
// Enumerate the source changes and retrieve the destination version for each source change.
foreach (ItemChange srcItem in sourceChanges)
{
ItemChange localVer;
// When the source item exists in the destination metadata store, retrieve the destination version of the item.
if (_items.ContainsKey(srcItem.ItemId))
{
XmlItemMetadata localMeta = _items[srcItem.ItemId];
ChangeKind kind = (localMeta.IsDeleted) ? ChangeKind.Deleted : ChangeKind.Update;
localVer = new ItemChange(IdFormats, ReplicaId, srcItem.ItemId, kind, localMeta.CreationVersion, localMeta.ChangeVersion);
}
// When the source item does not exist in the destination metadata store, create a new change with unknown
// version information.
else
{
localVer = new ItemChange(IdFormats, ReplicaId, srcItem.ItemId, ChangeKind.UnknownItem, SyncVersion.UnknownVersion, SyncVersion.UnknownVersion);
}
localVersions.Add(localVer);
}
return localVersions;
}
EndSession 方法
當來源提供者傳送了最後一個批次,而且目的地提供者也將變更套用至其資料存放區之後,Sync Framework 會在來源及目的地提供者上呼叫 EndSession。這個方法會通知提供者,它正要離開同步處理工作階段,而且會釋出任何與該工作階段相關聯的資源。這個實作會在 BeginSession 呼叫中釋放其儲存的工作階段狀態物件,或是在提供者之前未聯結同步處理工作階段時擲回 SyncInvalidOperationException。
public override void EndSession(SyncSessionContext syncSessionContext)
{
// If this object is not in a session, throw an exception.
if (null == _sessionContext)
{
throw new SyncInvalidOperationException();
}
_sessionContext = null;
}
未實作的方法
下列方法並不需要,因為此範例從不移除在中繼資料存放區中標示為刪除的項目。這些方法可以擲回 NotImplementedException:
實作 INotifyingChangeApplierTarget
當目的地提供者呼叫 ApplyChanges 方法時,此介面會提供給 Sync Framework,一般來說是在 ProcessChangeBatch 方法中。INotifyingChangeApplierTarget 包含變更套用期間所呼叫的方法。這些方法只在目的地提供者上呼叫。
宣告 INotifyingChangeApplierTarget
將 INotifyingChangeApplierTarget
加入至您的類別繼承清單。
class ContactsProviderXmlMetadataNoChangeUnits : KnowledgeSyncProvider
, INotifyingChangeApplierTarget
將 INotifyingChangeApplierTarget 方法加入至您的類別。
IdFormats 屬性
Sync Framework 會呼叫 IdFormats,以擷取提供者的識別碼格式結構描述。此範例會使用相同的類別來實作 KnowledgeSyncProvider 和 INotifyingChangeApplierTarget。因此,這個實作與上述 KnowledgeSyncProvider 之 IdFormats 屬性的實作相同。
GetNextTickCount
Sync Framework 會呼叫 GetNextTickCount,累加並擷取複寫的滴答計數。此實作會呼叫中繼資料存放區物件的 GetNextTickCount 方法。
public ulong GetNextTickCount()
{
return _itemStore.ContactReplicaMetadata.GetNextTickCount();
}
GetNextTickCount 的中繼資料存放區實作會累加並傳回複寫的滴答計數。
public override ulong GetNextTickCount()
{
return ++_tickCount;
}
SaveItemChange
在變更套用期間,Sync Framework 會針對每個要套用到目的地複寫的變更呼叫 SaveItemChange。這個實作會針對收到的每一種變更類型來更新項目存放區和中繼資料存放區。當建立或更新項目時,將會在 context
參數的 ChangeData 屬性中收到項目資料。ChangeData 屬性包含了從來源提供者的 LoadChangeData 方法所傳回的 object。在套用變更之後,便會儲存更新的目的地知識。
public void SaveItemChange(SaveChangeAction saveChangeAction, ItemChange change, SaveChangeContext context)
{
switch (saveChangeAction)
{
// Update the item store and metadata store when an item is created or updated.
case SaveChangeAction.Create:
case SaveChangeAction.UpdateVersionAndData:
{
try
{
_itemStore.UpdateContactFromSync(change, (string)context.ChangeData);
}
catch (Exception ex)
{
RecoverableErrorData errData = new RecoverableErrorData(ex);
context.RecordRecoverableErrorForItem(errData);
}
break;
}
// Update only the version of this item in the metadata store.
case SaveChangeAction.UpdateVersionOnly:
{
try
{
_itemStore.UpdateContactVersion(change.ItemId, change.ChangeVersion);
}
catch (Exception ex)
{
RecoverableErrorData errData = new RecoverableErrorData(ex);
context.RecordRecoverableErrorForItem(errData);
}
break;
}
// Delete the item from the item store and store a tombstone for it in the metadata store.
case SaveChangeAction.DeleteAndStoreTombstone:
{
try
{
_itemStore.DeleteContactFromSync(change.ItemId, change.ChangeVersion);
}
catch (Exception ex)
{
RecoverableErrorData errData = new RecoverableErrorData(ex);
context.RecordRecoverableErrorForItem(errData);
}
break;
}
// Neither merging of data nor removing tombstones is supported.
case SaveChangeAction.UpdateVersionAndMergeData:
case SaveChangeAction.DeleteAndRemoveTombstone:
{
throw new NotImplementedException();
}
default:
{
throw new ArgumentOutOfRangeException();
}
}
// Save the knowledge in the metadata store as each change is applied. Saving knowledge as each change is applied is
// not required. It is more robust than saving the knowledge only after each change batch, because if synchronization is interrupted
// before the end of a change batch, the knowledge will still reflect all of the changes applied. However, it is less efficient because
// knowledge must be stored more frequently.
SyncKnowledge knowledge;
ForgottenKnowledge forgottenKnowledge;
context.GetUpdatedDestinationKnowledge(out knowledge, out forgottenKnowledge);
_itemStore.ContactReplicaMetadata.SetKnowledge(knowledge);
}
在下列範例中,_ContactItemMetaList
包含 ItemMetadata 物件。
連絡人存放區的 UpdateContactFromSync
方法會更新指定的連絡人。如果此連絡人不存在,便會建立新的連絡人,並將其加入至連絡人存放區。中繼資料存放區也會更新,以反映連絡人存放區的變更。
public void UpdateContactFromSync(ItemChange itemChange, string changeData)
{
// If the item does not exist, create a new contact and add it to the contact and metadata store.
if (!_ContactList.ContainsKey(itemChange.ItemId))
{
Contact contact = new Contact();
ItemMetadata newItemMeta = _ContactReplicaMetadata.CreateItemMetadata(itemChange.ItemId,
itemChange.CreationVersion);
_ContactList.Add(newItemMeta.GlobalId, contact);
_ContactItemMetaList.Add(newItemMeta.GlobalId, newItemMeta);
}
// Update the specified contact in the contact store. changeData is the contact data returned by the
// IChangeDataRetriever.LoadChangeData method of the source provider.
_ContactList[itemChange.ItemId].FromString(changeData);
// Get the metadata for the specified item.
ItemMetadata itemMeta = _ContactItemMetaList[itemChange.ItemId];
// Update the index fields for the item. This implementation defines an index that uniquely identifies each contact.
// The index consists of the first name, last name, and phone number of the contact.
itemMeta.SetCustomField(FirstNameField, _ContactList[itemChange.ItemId].FirstName);
itemMeta.SetCustomField(LastNameField, _ContactList[itemChange.ItemId].LastName);
itemMeta.SetCustomField(PhoneNumberField, _ContactList[itemChange.ItemId].PhoneNumber);
// Update the version for the change.
itemMeta.ChangeVersion = itemChange.ChangeVersion;
}
連絡人存放區的 UpdateContactVersion
方法會更新指定之項目的版本中繼資料。
public void UpdateContactVersion(SyncId itemId, SyncVersion itemVersion)
{
// Update the version metadata for the specified item.
_ContactItemMetaList[itemId].ChangeVersion = itemVersion;
}
連絡人存放區的 DeleteContactFromSync
方法會從連絡人存放區中移除此項目,並在中繼資料存放區中將它標示為刪除。
public void DeleteContactFromSync(SyncId itemId, SyncVersion version)
{
if (_ContactList.ContainsKey(itemId))
{
// Remove the item from the contact store.
_ContactList.Remove(itemId);
// Mark the item as deleted in the metadata store.
ItemMetadata itemMeta = _ContactItemMetaList[itemId];
itemMeta.MarkAsDeleted(version);
// Change the first index field so the index fields don't collide with future items.
itemMeta.SetCustomField(FirstNameField, itemId.ToString());
// Move the metadata for the deleted item to a separate list.
// The deleted item metadata must be kept so that it can be committed when
// SaveChanges is called.
_ContactDeletedItemMetaList.Add(itemMeta);
_ContactItemMetaList.Remove(itemId);
}
else
{
// An item marked as deleted has been received as part of synchronization, but it does not exist in
// the item store. Create a tombstone for it in the metadata store.
ItemMetadata itemMeta = _ContactReplicaMetadata.CreateItemMetadata(itemId, version);
itemMeta.MarkAsDeleted(version);
// Clear the index fields so they don't collide with future items.
itemMeta.SetCustomField(FirstNameField, itemId.ToString());
_ContactDeletedItemMetaList.Add(itemMeta);
}
}
StoreKnowledgeForScope
處理了每個變更批次之後,Sync Framework 會呼叫 StoreKnowledgeForScope,以便讓目的地提供者儲存包含新變更的知識。此實作會將知識物件儲存至中繼資料存放區,並覆寫先前已有的知識。變更批次處理期間針對連絡人存放區和中繼資料存放區所做的變更會認可到磁碟上的檔案。
public void StoreKnowledgeForScope(SyncKnowledge knowledge, ForgottenKnowledge forgottenKnowledge)
{
_itemStore.ContactReplicaMetadata.SetKnowledge(knowledge);
// Commit changes made to the in-memory item store to the file on disk.
_itemStore.SaveContactChanges();
// Commit changes made to the in-memory metadata store to the file on disk.
_itemStore.SaveMetadataChanges();
}
連絡人存放區的 SaveMetadataChanges
方法會將針對中繼資料存放區所做的變更認可到磁碟上的檔案。
public void SaveMetadataChanges()
{
// A transaction is required for saving changes to the metadata store.
_ContactMetadataStore.BeginTransaction(IsolationLevel.ReadCommitted);
// Enumerate the deleted items list.
if (null != _ContactDeletedItemMetaList)
{
foreach (ItemMetadata contactMeta in _ContactDeletedItemMetaList)
{
// Save the deleted item metadata to the metadata store.
_ContactReplicaMetadata.SaveItemMetadata(contactMeta);
}
}
// Save renamed items first to avoid collisions in the metadata store.
foreach (SyncId itemId in _ContactRenameList)
{
_ContactReplicaMetadata.SaveItemMetadata(_ContactItemMetaList[itemId]);
}
// Enumerate the active contacts.
for (int iCon = 0; iCon < _ContactItemMetaList.Count; iCon++)
{
// Save the item metadata to the metadata store.
_ContactReplicaMetadata.SaveItemMetadata(_ContactItemMetaList.Values[iCon]);
}
// Save the replica metadata to the metadata store.
_ContactReplicaMetadata.SaveReplicaMetadata();
// Commit the metadata store transaction.
_ContactMetadataStore.CommitTransaction();
}
未實作的方法
下列方法並不是基本同步處理案例所需要的方法,而且會擲回 NotImplementedException:
實作 IChangeDataRetriever
IChangeDataRetriever 是由來源提供者傳回 Sync Framework,以回應 GetChangeBatch 呼叫。IChangeDataRetriever 是在 ProcessChangeBatch 呼叫中傳送至目的地提供者,一般是傳遞至變更套用者的 ApplyChanges 方法。然後,此變更套用者會呼叫 LoadChangeData,取得代表項目資料的 object。此變更套用者會將此介面傳遞至目的地提供者的 SaveItemChange 或 SaveChangeWithChangeUnits 方法。目的地提供者會使用此 object,擷取新增或變更之項目的項目資料,然後將項目資料套用至目的地複寫。
宣告 IChangeDataRetriever
將 IChangeDataRetriever
加入至類別繼承清單。
class ContactsProviderXmlMetadataNoChangeUnits : KnowledgeSyncProvider
, INotifyingChangeApplierTarget
, IChangeDataRetriever
將 IChangeDataRetriever 方法加入至此類別。
IdFormats 屬性
Sync Framework 會呼叫 IdFormats,以擷取提供者的識別碼格式結構描述。此範例會使用相同的類別來實作 KnowledgeSyncProvider 和 IChangeDataRetriever。因此,這個實作與上述 KnowledgeSyncProvider 之 IdFormats 屬性的實作相同。
LoadChangeData 方法
在變更套用期間,Sync Framework 會呼叫 LoadChangeData,取得目的地提供者可以用來擷取項目資料的 object。這個實作會傳回序列化為一個字串的連絡人資料。
public object LoadChangeData(LoadChangeContext loadChangeContext)
{
// Return the specified contact serialized as a string.
return _itemStore.ContactList[loadChangeContext.ItemChange.ItemId].ToString();
}
後續的步驟
現在,您可能要建立可裝載同步處理工作階段的應用程式,並將它連接至提供者。如需如何執行這項操作的詳細資訊,請參閱 HOW TO:建立 Unmanaged 同步處理應用程式。
您也可以增強提供者來篩選項目或同步處理的變更單位。如需篩選的詳細資訊,請參閱篩選同步處理資料。
您也可以增強提供者來處理變更單位。如需變更單位的詳細資訊,請參閱同步處理變更單位。
您可能也要建立自訂中繼資料存放區。如需如何處置同步處理中繼資料的詳細資訊,請參閱管理標準提供者的中繼資料。
請參閱
參考
SyncProvider
KnowledgeSyncProvider
INotifyingChangeApplierTarget
IChangeDataRetriever
NotifyingChangeApplier