Como criar um provedor de sincronização não gerenciado
Este tópico mostra como usar uma linguagem não gerenciada, como C++, para criar um provedor de sincronização do Sync Framework que sincroniza dados de um repositório de dados personalizado.
Este tópico pressupõe familiaridade básica com C++ e conceitos de COM.
Os exemplos neste tópico se concentram nas seguintes interfaces do Sync Framework:
Noções básicas sobre provedores de sincronização
Um provedor de sincronização é um componente de software que representa uma réplica durante a sincronização. Isso permite que a réplica sincronize os dados com outras réplicas. Para que a sincronização ocorra, um aplicativo cria um objeto de sessão de sincronização, conecta-o a dois objetos ISyncProvider e inicia a sessão. Um dos provedores representa a réplica de origem. A réplica de origem fornece metadados para itens alterados por meio do método IKnowledgeSyncProvider::GetChangeBatch e dados de item por meio de um objeto ISynchronousDataRetriever. O outro provedor representa a réplica de destino. A réplica de destino recebe metadados para itens alterados por meio do método IKnowledgeSyncProvider::ProcessChangeBatch e aplica as alterações ao repositório de itens usando um objeto ISynchronousChangeApplier fornecido pelo Sync Framework junto com seu próprio objeto ISynchronousChangeApplierTarget.
Para obter mais informações sobre a função do provedor de sincronização, consulte Implementando um provedor personalizado padrão.
Requisitos de compilação
Declarações Synchronization.h: para componentes do Sync Framework.
#include <synchronization.h>
Códigos de erro personalizados Synchronizationerrors.h:.
#include <synchronizationerrors.h>
Biblioteca de importação Synchronization.lib:.
Exemplo
O código de exemplo neste tópico mostra como implementar os métodos de interface básicos necessários para que uma réplica participe de uma comunidade de sincronização do Sync Framework como uma origem e como um destino. A réplica neste exemplo é um arquivo XML e os itens que devem ser sincronizados são nós XML contidos nesse arquivo. No código, os nós XML são representados pela interface IXMLDOMNode
. Este exemplo também usa um repositório de metadados personalizado implementado pela API do serviço de armazenamento de metadados. Para obter informações sobre o serviço de armazenamento de metadados e outros componentes do Sync Framework, consulte Sync Framework Metadata Storage Service.
Os repositórios de metadados e de XML são declarados como membros da classe do provedor.
CMetadataMgr* m_pMetadataMgr;
CItemStore* m_pItemStore;
Implementando ISyncProvider e IKnowledgeSyncProvider
O ponto de entrada do provedor é a interface ISyncProvider. Essa interface foi desenvolvida para funcionar como uma classe base para outras interfaces de provedor mais avançadas. Este exemplo usa a interface IKnowledgeSyncProvider.
Declarando IKnowledgeSyncProvider
Adicione IKnowledgeSyncProvider
à lista de herança de classe.
class CXMLProvider : public IKnowledgeSyncProvider
Adicione os métodos ISyncProvider
à declaração de classe.
STDMETHOD(GetIdParameters)(
ID_PARAMETERS * pIdParameters);
Adicione os métodos IKnowledgeSyncProvider à declaração de classe.
STDMETHOD(BeginSession)(
SYNC_PROVIDER_ROLE role,
ISyncSessionState * pSessionState);
STDMETHOD(GetSyncBatchParameters)(
ISyncKnowledge ** ppSyncKnowledge,
DWORD * pdwRequestedBatchSize);
STDMETHOD(GetChangeBatch)(
DWORD dwBatchSize,
ISyncKnowledge * pSyncKnowledge,
ISyncChangeBatch ** ppSyncChangeBatch,
IUnknown ** ppUnkDataRetriever);
STDMETHOD(GetFullEnumerationChangeBatch)(
DWORD dwBatchSize,
const BYTE * pbLowerEnumerationBound,
ISyncKnowledge * pSyncKnowledgeForDataRetrieval,
ISyncFullEnumerationChangeBatch ** ppSyncChangeBatch,
IUnknown ** ppUnkDataRetriever);
STDMETHOD(ProcessChangeBatch)(
CONFLICT_RESOLUTION_POLICY resolutionPolicy,
ISyncChangeBatch * pSourceChangeBatch,
IUnknown * pUnkDataRetriever,
ISyncCallback * pCallback,
SYNC_SESSION_STATISTICS * pSyncSessionStatistics);
STDMETHOD(ProcessFullEnumerationChangeBatch)(
CONFLICT_RESOLUTION_POLICY resolutionPolicy,
ISyncFullEnumerationChangeBatch * pSourceChangeBatch,
IUnknown * pUnkDataRetriever,
ISyncCallback * pCallback,
SYNC_SESSION_STATISTICS * pSyncSessionStatistics);
STDMETHOD(EndSession)(
ISyncSessionState * pSessionState);
Método GetIdParameters
O Sync Framework chama ISyncProvider::GetIdParameters nos provedores de origem e de destino quando o objeto ISyncSession é criado. Esse método retorna o esquema de formato de ID usado pelo provedor. O esquema deve ser o mesmo para ambos os provedores. A implementação neste exemplo usa uma constante global, pois os formatos de ID são constantes para o provedor.
const ID_PARAMETERS c_idParams =
{
sizeof(ID_PARAMETERS), // dwSize
{ FALSE, sizeof(GUID) }, // replicaId
{ FALSE, sizeof(SYNC_GID) }, // itemId
{ FALSE, 1 }, // changeUnitId
};
O uso de uma constante global facilita a implementação do método.
STDMETHODIMP CXMLProvider::GetIdParameters(
ID_PARAMETERS * pIdParameters)
{
if (NULL == pIdParameters)
{
return E_POINTER;
}
else
{
*pIdParameters = c_idParams;
return S_OK;
}
}
Método BeginSession
Em seguida, o Sync Framework chama IKnowledgeSyncProvider::BeginSession nos provedores de origem e de destino. O método informa a um provedor que está se juntando a uma sessão de sincronização e transmite para o provedor um objeto que contém informações sobre o estado da sessão. Esta implementação armazena o objeto de estado de sessão.
STDMETHODIMP CXMLProvider::BeginSession(
SYNC_PROVIDER_ROLE role,
ISyncSessionState * pSessionState)
{
HRESULT hr = E_UNEXPECTED;
if (NULL == pSessionState)
{
hr = E_POINTER;
}
else
{
// This method should not be called twice.
if (NULL != m_pSessionState || NULL == m_pMetadataMgr)
{
hr = SYNC_E_INVALID_OPERATION;
}
else
{
// Store the role and the session state object.
m_role = role;
pSessionState->AddRef();
m_pSessionState = pSessionState;
hr = S_OK;
}
}
return hr;
}
Método GetSyncBatchParameters
O Sync Framework chama IKnowledgeSyncProvider::GetSyncBatchParameters no provedor de destino. Este método recupera o número de alterações que o provedor de origem deve incluir em um lote de alterações e obtém o conhecimento atual do provedor de destino. A implementação extrai o conhecimento do repositório de metadados e define o tamanho do lote como 10
.
STDMETHODIMP CXMLProvider::GetSyncBatchParameters(
ISyncKnowledge ** ppSyncKnowledge,
DWORD * pdwRequestedBatchSize)
{
HRESULT hr = E_UNEXPECTED;
if (NULL == ppSyncKnowledge || NULL == pdwRequestedBatchSize)
{
hr = E_POINTER;
}
else
{
_ASSERT(NULL != m_pMetadataMgr);
*pdwRequestedBatchSize = 10;
hr = m_pMetadataMgr->GetKnowledge(ppSyncKnowledge);
}
return hr;
}
Método GetChangeBatch
A sessão de sincronização tem início quando o Sync Framework chama IKnowledgeSyncProvider::GetChangeBatch no provedor de origem. Este método recupera um lote de alterações para ser enviado para o provedor de destino e também retorna a interface de recuperador de dados. O provedor de destino usa essa interface para recuperar dados de item para alterações aplicadas à réplica de destino. O Sync Framework chama GetChangeBatch
repetidamente até o envio do último lote. O provedor de origem indica que um lote é o último lote ligar o ISyncChangeBatchBase::SetLastBatch método. Esta implementação delega a tarefa de enumeração de alteração ao método GetChangeBatch
do repositório de metadados. O objeto de repositório de itens XML implementa uma interface de recuperador de dados; consequentemente, a interface IUnknown
é retornada.
STDMETHODIMP CXMLProvider::GetChangeBatch(
DWORD dwBatchSize,
ISyncKnowledge * pSyncKnowledge,
ISyncChangeBatch ** ppSyncChangeBatch,
IUnknown ** ppUnkDataRetriever)
{
HRESULT hr = E_UNEXPECTED;
if (NULL == pSyncKnowledge || NULL == ppSyncChangeBatch || NULL == ppUnkDataRetriever)
{
hr = E_POINTER;
}
else
{
_ASSERT(NULL != m_pMetadataMgr);
hr = m_pMetadataMgr->GetChangeBatch(dwBatchSize, pSyncKnowledge, ppSyncChangeBatch);
if (SUCCEEDED(hr))
{
hr = m_pItemStore->QueryInterface(IID_IUnknown, (void**)ppUnkDataRetriever);
}
}
return hr;
}
O método GetChangeBatch
implementado pelo repositório de metadados enumera os itens no repositório de metadados e verifica a versão de cada um com o conhecimento do destino. Se a réplica de destino não souber de uma alteração, a alteração será adicionada ao lote de alterações retornado.
STDMETHODIMP CMetadataMgr::GetChangeBatch(
DWORD dwBatchSize,
ISyncKnowledge *pSyncKnowledge,
ISyncChangeBatch ** ppSyncChangeBatch)
{
HRESULT hr = E_UNEXPECTED;
ISyncChangeBatch* pChangeBatch = NULL;
ISyncKnowledge* pMappedDestKnowledge = NULL;
ISyncKnowledge* pSourceKnowledge = NULL;
if (NULL == pSyncKnowledge || NULL == ppSyncChangeBatch)
{
hr = E_POINTER;
}
else
{
// Get our (source) knowledge object, map the remote (destination) knowledge for local use,
// and get our replica ID.
GUID guidReplicaID;
hr = GetKnowledge(&pSourceKnowledge);
if (SUCCEEDED(hr))
{
hr = pSourceKnowledge->MapRemoteToLocal(pSyncKnowledge, &pMappedDestKnowledge);
if (SUCCEEDED(hr))
{
ULONG cbID = sizeof(guidReplicaID);
hr = GetReplicaId((BYTE*)&guidReplicaID, &cbID);
}
}
if (SUCCEEDED(hr))
{
// Create a new change batch object. We'll fill this object with changes to send.
IProviderSyncServices* pProvSvc = NULL;
// This helper function creates and initializes the IProviderSyncServices interface.
hr = GetProviderSyncServices(&c_idParams, &pProvSvc);
if (SUCCEEDED(hr))
{
hr = pProvSvc->CreateChangeBatch(pSyncKnowledge, NULL, &pChangeBatch);
pProvSvc->Release();
pProvSvc = NULL;
}
}
// Enumerate the items in our store and add new changes to the change batch.
if (SUCCEEDED(hr))
{
// Begin an unordered group in our change batch. All change items will be added to this group.
hr = pChangeBatch->BeginUnorderedGroup();
if (SUCCEEDED(hr))
{
ULONG cFetched = 1;
IItemMetadata* pItemMeta = NULL;
SYNC_GID gidItem;
ULONG cbgid = sizeof(gidItem);
SYNC_VERSION verCur;
SYNC_VERSION verCreate;
hr = Reset();
while (S_OK == hr)
{
hr = Next(1, &pItemMeta, &cFetched);
if (S_OK == hr)
{
hr = pItemMeta->GetGlobalId((BYTE*)&gidItem, &cbgid);
if (SUCCEEDED(hr))
{
hr = pItemMeta->GetChangeVersion(&verCur);
if (SUCCEEDED(hr))
{
// Find out whether the destination already knows about this change.
hr = pMappedDestKnowledge->ContainsChange((BYTE*)&guidReplicaID,
(BYTE*)&gidItem, &verCur);
if (S_FALSE == hr)
{
// S_FALSE means the destination does not know about the
// change, so add it to the change batch.
DWORD dwFlags = 0;
BOOL fTomb = 0;
hr = pItemMeta->GetIsDeleted(&fTomb);
if (fTomb)
{
dwFlags = SYNC_CHANGE_FLAG_DELETED;
}
hr = pItemMeta->GetCreationVersion(&verCreate);
if (SUCCEEDED(hr))
{
hr = pChangeBatch->AddItemMetadataToGroup((BYTE*)&guidReplicaID,
(BYTE*)&gidItem, &verCur, &verCreate, dwFlags, 0, NULL);
}
}
}
}
pItemMeta->Release();
}
}
}
if (SUCCEEDED(hr))
{
// We always send the entire set of changes, so every batch is the last batch.
// If this flag is not set Sync Framework will call GetChangeBatch again.
hr = pChangeBatch->SetLastBatch();
}
if (SUCCEEDED(hr))
{
// Close the change batch group that contains our changes.
hr = pChangeBatch->EndUnorderedGroup(pSourceKnowledge, TRUE);
}
}
if (NULL != pChangeBatch)
{
if (SUCCEEDED(hr))
{
// Return the change batch we've constructed. This will be sent to the
// destination provider.
*ppSyncChangeBatch = pChangeBatch;
}
else
{
pChangeBatch->Release();
}
}
if (NULL != pMappedDestKnowledge)
{
pMappedDestKnowledge->Release();
}
if (NULL != pSourceKnowledge)
{
pSourceKnowledge->Release();
}
}
return hr;
}
Método ProcessChangeBatch
Depois que o Sync Framework tiver obtido um lote de alterações do provedor de origem chamando o método GetChangeBatch
, o Sync Framework chama IKnowledgeSyncProvider::ProcessChangeBatch no provedor de destino. Este método aplica as alterações à réplica de destino. Este método é chamado uma vez para cada lote recuperado usando GetChangeBatch
do provedor de origem. Esta implementação usa o método GetItemBatchVersions
do repositório de metadados para obter informações de versão local de itens do provedor de origem. Ele cria um objeto ISynchronousNotifyingChangeApplier implementado por Sync Framework e chama o método ISynchronousNotifyingChangeApplier::ApplyChanges.
STDMETHODIMP CXMLProvider::ProcessChangeBatch(
CONFLICT_RESOLUTION_POLICY resolutionPolicy,
ISyncChangeBatch * pSourceChangeBatch,
IUnknown * pUnkDataRetriever,
ISyncCallback * pCallback,
SYNC_SESSION_STATISTICS * pSyncSessionStatistics)
{
HRESULT hr = E_UNEXPECTED;
if (NULL == pSourceChangeBatch || NULL == pUnkDataRetriever || NULL == pSyncSessionStatistics)
{
hr = E_POINTER;
}
else
{
IEnumSyncChanges* pDestinationChangeEnum = NULL;
_ASSERT(NULL != m_pMetadataMgr);
// Obtain the local (destination) versions for the items in the source change batch.
hr = m_pMetadataMgr->GetItemBatchVersions(pSourceChangeBatch, &pDestinationChangeEnum);
if (SUCCEEDED(hr))
{
IProviderSyncServices* pProviderSvc = NULL;
hr = GetProviderSyncServices(&c_idParams, &pProviderSvc);
if (SUCCEEDED(hr))
{
// Create a standard change applier from Sync Framework.
ISynchronousNotifyingChangeApplier* pChangeApplier = NULL;
hr = pProviderSvc->CreateChangeApplier(IID_ISynchronousNotifyingChangeApplier,
(void**)&pChangeApplier);
if (SUCCEEDED(hr))
{
ISyncKnowledge* pDestinationKnowledge = NULL;
hr = m_pMetadataMgr->GetKnowledge(&pDestinationKnowledge);
if (SUCCEEDED(hr))
{
// Have the change applier process the change batch and apply changes.
// This method will call the change applier target methods to save
// changes and conflicts. It will also pass the data retriever
// interface to the change applier target so it can retrieve item data.
hr = pChangeApplier->ApplyChanges(resolutionPolicy, pSourceChangeBatch,
pUnkDataRetriever, pDestinationChangeEnum, pDestinationKnowledge,
NULL, this, m_pSessionState, pCallback);
pDestinationKnowledge->Release();
}
pChangeApplier->Release();
}
pProviderSvc->Release();
}
pDestinationChangeEnum->Release();
}
}
return hr;
}
O método GetItemBatchVersions
do repositório de metadados enumera as alterações enviadas no lote de alterações do provedor de origem. Se um item estiver nos metadados de destino, suas informações de versão serão adicionadas a um novo lote criado unicamente para reter as informações de versão. Se um item não existir nos metadados de destino, ele será sinalizado como um novo item no lote de versão. O método retorna o lote de versão.
STDMETHODIMP CMetadataMgr::GetItemBatchVersions(
ISyncChangeBatch * pRemoteSyncChangeBatch,
IEnumSyncChanges ** ppLocalVersionsEnum)
{
HRESULT hr = E_UNEXPECTED;
if (NULL == pRemoteSyncChangeBatch || NULL == ppLocalVersionsEnum)
{
hr = E_POINTER;
}
else
{
IProviderSyncServices* pProvSvc;
hr = GetProviderSyncServices(&c_idParams, &pProvSvc);
if (SUCCEEDED(hr))
{
IDestinationChangeVersionsBuilder* pDestChangeBuilder = NULL;
hr = pProvSvc->CreateDestinationChangeVersionsBuilder(&pDestChangeBuilder);
if (SUCCEEDED(hr))
{
IEnumSyncChanges* pRemoteEnum = NULL;
hr = pRemoteSyncChangeBatch->GetChangeEnumerator(&pRemoteEnum);
if (SUCCEEDED(hr))
{
ULONG cFetched;
ISyncChange* pChange;
SYNC_GID gidItem;
DWORD cbID = sizeof(gidItem);
DWORD dwFlags;
SYNC_VERSION verCurrent;
SYNC_VERSION verCreation;
HRESULT hrEnum = S_OK;
while (S_OK == hrEnum && SUCCEEDED(hr))
{
pChange = NULL;
hrEnum = pRemoteEnum->Next(1, &pChange, &cFetched);
if (S_OK == hrEnum)
{
hr = pChange->GetRootItemId((BYTE*)&gidItem, &cbID);
if (SUCCEEDED(hr))
{
// Try to find the item in the local (destination) metadata.
IItemMetadata* pItem = NULL;
hr = FindItemMetadataByGlobalId((BYTE*)&gidItem, &pItem);
if (S_OK == hr)
{
// S_OK means the item exists in our local store.
// Extract its version and tombstone information.
dwFlags = 0;
BOOL fTombstone = FALSE;
hr = pItem->GetIsDeleted(&fTombstone);
if (SUCCEEDED(hr))
{
if (fTombstone)
{
dwFlags = SYNC_CHANGE_FLAG_DELETED;
}
}
if (SUCCEEDED(hr))
{
hr = pItem->GetChangeVersion(&verCurrent);
if (SUCCEEDED(hr))
{
hr = pItem->GetCreationVersion(&verCreation);
}
}
pItem->Release();
}
else if (S_FALSE == hr)
{
// S_FALSE means this item does not exist in our local store.
// Set versions to 0 and flag it as a new item.
verCurrent.dwLastUpdatingReplicaKey = 0;
verCurrent.ullTickCount = 0;
verCreation.dwLastUpdatingReplicaKey = 0;
verCreation.ullTickCount = 0;
dwFlags = SYNC_CHANGE_FLAG_DOES_NOT_EXIST;
}
if (SUCCEEDED(hr))
{
// Add the item to the batch of destination versions.
GUID guidReplicaID = GUID_NULL;
ULONG cbID = sizeof(guidReplicaID);
hr = GetReplicaId((BYTE*)&guidReplicaID, &cbID);
if (SUCCEEDED(hr))
{
hr = pDestChangeBuilder->AddItemMetadata((BYTE*)&guidReplicaID,
(BYTE*)&gidItem, &verCurrent, &verCreation, dwFlags, NULL);
}
}
}
pChange->Release();
}
}
if (FAILED(hrEnum))
{
hr = hrEnum;
}
pRemoteEnum->Release();
}
if (SUCCEEDED(hr))
{
hr = pDestChangeBuilder->GetChangeEnumerator(ppLocalVersionsEnum);
}
pDestChangeBuilder->Release();
}
pProvSvc->Release();
}
}
return hr;
}
Método EndSession
Depois que o provedor de origem envia o último lote e o provedor de destino aplica as alterações ao repositório de dados, o Sync Framework chama IKnowledgeSyncProvider::EndSession nos provedores de origem e de destino. Este método informa a um provedor que está saindo de uma sessão de sincronização e deve liberar os recursos associados à sessão. Esta implementação libera o objeto de estado de sessão armazenado na chamada BeginSession
.
STDMETHODIMP CXMLProvider::EndSession(
ISyncSessionState * pSessionState)
{
HRESULT hr = E_UNEXPECTED;
if (NULL == m_pSessionState)
{
hr = SYNC_E_INVALID_OPERATION;
}
else
{
m_pSessionState->Release();
m_pSessionState = NULL;
hr = S_OK;
}
return hr;
}
Métodos não implementados
Os métodos a seguir não são necessários, pois este exemplo nunca remove itens marcados como excluídos no repositório de metadados. Estes métodos podem retornar E_NOTIMPL:
Implementando ISynchronousNotifyingChangeApplierTarget
Esta interface é fornecida para o Sync Framework quando o provedor de destino chama o método ISynchronousNotifyingChangeApplier::ApplyChanges, normalmente no método ProcessChangeBatch. ISynchronousNotifyingChangeApplierTarget contém métodos chamados durante a alteração de aplicativo. Esses métodos só são chamados no provedor de destino.
Declarando ISynchronousNotifyingChangeApplierTarget
Adicione ISynchronousNotifyingChangeApplierTarget
à lista de herança de classe.
class CXMLProvider : public IKnowledgeSyncProvider
, ISynchronousNotifyingChangeApplierTarget
Adicione os métodos ISynchronousNotifyingChangeApplierTarget
à declaração de classe.
STDMETHOD(GetDataRetriever)(
IUnknown ** ppDataRetriever);
STDMETHOD(GetCurrentTickCount)(
ULONGLONG * pTickCount);
STDMETHOD(GetDestinationVersion)(
ISyncChange * pSourceChange,
ISyncChange ** ppDestinationVersion);
STDMETHOD(SaveChange)(
SYNC_SAVE_ACTION ssa,
ISyncChange * pChange,
ISaveChangeContext * pSaveContext);
STDMETHOD(SaveChangeWithChangeUnits)(
ISyncChange * pChange,
ISaveChangeWithChangeUnitsContext * pSaveContext);
STDMETHOD(SaveConflict)(
ISyncChange * pChange,
IUnknown * pUnkData,
ISyncKnowledge * pConflictKnowledge);
STDMETHOD(SaveKnowledge)(
ISyncKnowledge * pSyncKnowledge,
IForgottenKnowledge * pForgottenKnowledge);
Método GetIdParameters
O Sync Framework chama ISynchronousNotifyingChangeApplierTarget::GetIdParameters para recuperar o esquema de formato de ID do provedor. Este exemplo usa a mesma classe para implementar o IKnowledgeSyncProvider
e o ISynchronousNotifyingChangeApplierTarget
. Portanto, esta implementação é a mesma para ISyncProvider::GetIdParameters
.
STDMETHODIMP CXMLProvider::GetIdParameters(
ID_PARAMETERS * pIdParameters)
{
if (NULL == pIdParameters)
{
return E_POINTER;
}
else
{
*pIdParameters = c_idParams;
return S_OK;
}
}
GetCurrentTickCount
O Sync Framework chama ISynchronousNotifyingChangeApplierTarget::GetCurrentTickCount para incrementar e recuperar a contagem em escala da réplica. Esta implementação chama o método GetNextTickCount
do repositório de metadados.
STDMETHODIMP CXMLProvider::GetCurrentTickCount(
ULONGLONG * pTickCount)
{
_ASSERT(NULL != m_pMetadataMgr);
return m_pMetadataMgr->GetNextTickCount(pTickCount);
}
O método GetNextTickCount
do repositório de metadados incrementa e retorna a contagem em escala da réplica.
STDMETHODIMP CMetadataMgr::GetNextTickCount(
ULONGLONG * pNextTickCount)
{
HRESULT hr = E_UNEXPECTED;
if (NULL == pNextTickCount)
{
hr = E_POINTER;
}
else
{
// Get the local tick count, increment it, store it, and return it.
ULONGLONG ullTickCount = -1;
hr = GetTickCount(&ullTickCount);
if (SUCCEEDED(hr))
{
++ullTickCount;
hr = SetTickCount(ullTickCount);
if (SUCCEEDED(hr))
{
*pNextTickCount = ullTickCount;
}
}
}
return hr;
}
SaveChange
Durante uma aplicação de alteração, o Sync Framework chama ISynchronousNotifyingChangeApplierTarget::SaveChange para cada alteração que será se aplicada à réplica de destino. Esta implementação trata apropriadamente os itens novos, alterados e excluídos; ela também atualiza os dados de item no repositório de itens e os metadados de item no repositório de metadados.
STDMETHODIMP CXMLProvider::SaveChange(
SYNC_SAVE_ACTION ssa,
ISyncChange * pChange,
ISaveChangeContext * pSaveContext)
{
HRESULT hr = E_UNEXPECTED;
_ASSERT(NULL != m_pItemStore);
if (NULL == pChange || NULL == pSaveContext)
{
hr = E_POINTER;
}
else
{
// First save or delete the item data itself.
switch (ssa)
{
case SSA_DELETE_AND_REMOVE_TOMBSTONE:
{
// This sample does not track forgotten knowledge and so cannot properly
// handle this action.
hr = E_UNEXPECTED;
break;
}
case SSA_CREATE:
case SSA_UPDATE_VERSION_AND_DATA:
case SSA_UPDATE_VERSION_AND_MERGE_DATA:
{
// Save the item in the data store.
// This IUnknown interface is the interface returned by the data retriever's
// LoadChangeData method.
IUnknown* pUnk = NULL;
hr = pSaveContext->GetChangeData(&pUnk);
if (S_OK == hr)
{
// The item is an XML node.
IXMLDOMNode* pNode = NULL;
hr = pUnk->QueryInterface(__uuidof(pNode), (void**)&pNode);
if (SUCCEEDED(hr))
{
// Have the data store save the item.
hr = m_pItemStore->SaveItem(pChange, pNode);
pNode->Release();
}
pUnk->Release();
}
break;
}
case SSA_DELETE_AND_STORE_TOMBSTONE:
{
// Delete the item from the data store.
hr = m_pItemStore->DeleteItem(pChange);
}
break;
case SSA_UPDATE_VERSION_ONLY:
{
// Update the version only, so nothing to do in the data store.
hr = S_OK;
}
break;
default:
hr = E_INVALIDARG;
}
// Now update the metadata for the item in the metadata store.
if (SUCCEEDED(hr))
{
SYNC_GID gidItem;
DWORD cbItemID = sizeof(gidItem);
hr = pChange->GetRootItemId((BYTE*)&gidItem, &cbItemID);
if (SUCCEEDED(hr))
{
// Save the item metadata to the metadata store.
// First extract the information from the change.
GUID guidReplicaID;
ULONG cbReplicaID = sizeof(guidReplicaID);
hr = m_pMetadataMgr->GetReplicaId((BYTE*)&guidReplicaID, &cbReplicaID);
if (SUCCEEDED(hr))
{
SYNC_VERSION verCurrent;
hr = pChange->GetChangeVersion((BYTE*)&guidReplicaID, &verCurrent);
if (SUCCEEDED(hr))
{
SYNC_VERSION verCreation;
hr = pChange->GetCreationVersion((BYTE*)&guidReplicaID, &verCreation);
if (SUCCEEDED(hr))
{
DWORD dwFlags;
hr = pChange->GetFlags(&dwFlags);
if (SUCCEEDED(hr))
{
// Try to find the item in the metadata store.
IItemMetadata* pItem = NULL;
hr = m_pMetadataMgr->FindItemMetadataByGlobalId((BYTE*)&gidItem,
&pItem);
if (S_FALSE == hr)
{
// S_FALSE means the item does not exist in the metadata store.
// Therefore it must be a new item. Create it and set its
// creation version.
hr = m_pMetadataMgr->CreateNewItemMetadata(&pItem);
if (SUCCEEDED(hr))
{
hr = pItem->SetGlobalId((BYTE*)&gidItem);
if (SUCCEEDED(hr))
{
hr = pItem->SetCreationVersion(&verCreation);
}
}
}
// Set the item's change version and tombstone status.
if (SUCCEEDED(hr))
{
if (dwFlags & SYNC_CHANGE_FLAG_DELETED)
{
hr = pItem->MarkAsDeleted(&verCurrent);
}
else
{
hr = pItem->SetChangeVersion(&verCurrent);
}
}
// Commit the item change and update the knowledge.
if (SUCCEEDED(hr))
{
hr = m_pMetadataMgr->SaveItemMetadata(pItem);
if (SUCCEEDED(hr))
{
ISyncKnowledge* pUpdatedKnowledge = NULL;
IForgottenKnowledge* pUpdatedForgottenKnowledge = NULL;
hr = pSaveContext->GetKnowledgeForScope(&pUpdatedKnowledge, &pUpdatedForgottenKnowledge);
if (SUCCEEDED(hr))
{
hr = m_pMetadataMgr->SetKnowledge(pUpdatedKnowledge);
pUpdatedKnowledge->Release();
if (NULL != pUpdatedForgottenKnowledge)
{
// This sample does not use forgotten knowledge, so it is an error to receive
// forgotten knowledge from the save context.
hr = E_UNEXPECTED;
pUpdatedForgottenKnowledge->Release();
}
}
}
}
if (NULL != pItem)
{
pItem->Release();
}
}
}
}
}
}
}
}
return hr;
}
SaveKnowledge
Depois de processar cada lote de alterações, o Sync Framework chama ISynchronousNotifyingChangeApplierTarget::SaveKnowledge de forma que o provedor de destino possa salvar o conhecimento que contém as novas alterações. Esta implementação salva o objeto de conhecimento no repositório de metadados e substitui o conhecimento prévio existente.
STDMETHODIMP CXMLProvider::SaveKnowledge(
ISyncKnowledge * pSyncKnowledge,
IForgottenKnowledge * pForgottenKnowledge)
{
HRESULT hr = E_UNEXPECTED;
_ASSERT(NULL != m_pMetadataMgr);
if (NULL == pSyncKnowledge)
{
hr = E_POINTER;
}
else if (NULL != pForgottenKnowledge)
{
// This sample does not support forgotten knowledge, so it is an error to receive it in this method.bb
hr = E_INVALIDARG;
}
else
{
hr = m_pMetadataMgr->SetKnowledge(pSyncKnowledge);
}
return hr;
}
Métodos não implementados
Os métodos a seguir não são necessários para cenários de sincronização básica e retornam somente E_NOTIMPL:
ISynchronousNotifyingChangeApplierTarget::GetDestinationVersion
ISynchronousNotifyingChangeApplierTarget::SaveChangeWithChangeUnits
Implementando ISynchronousDataRetriever
ISynchronousDataRetriever é retornado ao Sync Framework pelo provedor de origem em resposta à chamada GetChangeBatch. ISynchronousDataRetriever é enviado para o provedor de destino na chamada ProcessChangeBatch, na qual é normalmente passado para o método ApplyChanges de um aplicador de alterações. O aplicador de alterações chama ISynchronousDataRetriever::LoadChangeData para obter uma interface IUnknown que representa os dados do item. O aplicador de alterações passa essa interface para o método SaveChange do provedor de destino. O provedor de destino usa a interface IUnknown para recuperar dados de item para itens novos ou alterados e aplica os dados à réplica de destino.
Declarando ISynchronousDataRetriever
Adicione ISynchronousDataRetriever
à lista de herança de classe.
class CItemStore : public ISynchronousDataRetriever
Adicione os métodos ISynchronousDataRetriever
à declaração de classe.
STDMETHOD(GetIdParameters)(
ID_PARAMETERS * pIdParameters);
STDMETHOD(LoadChangeData)(
ILoadChangeContext * pLoadChangeContext,
IUnknown ** ppUnkData);
Método GetIdParameters
O Sync Framework chama ISynchronousDataRetriever::GetIdParameters para recuperar o esquema de formato de ID do provedor. Esta implementação é basicamente a mesma de ISyncProvider::GetIdParameters
.
STDMETHODIMP CItemStore::GetIdParameters(
ID_PARAMETERS * pIdParameters)
{
if (NULL == pIdParameters)
{
return E_POINTER;
}
else
{
*pIdParameters = c_idParams;
return S_OK;
}
}
Método LoadChangeData
Durante a aplicação de alterações, o Sync Framework chama ISynchronousDataRetriever::LoadChangeData para obter uma interface IUnknown
que o provedor de destino pode usar para recuperar dados de item. Esta implementação encontra o item no repositório de item, clona-o e retorna sua interface IUnknown
.
STDMETHODIMP CItemStore::LoadChangeData(
ILoadChangeContext * pLoadChangeContext,
IUnknown ** ppUnkData)
{
HRESULT hr = E_UNEXPECTED;
if (NULL == pLoadChangeContext || NULL == ppUnkData)
{
hr = E_POINTER;
}
else
{
// Find the item in the data store, clone it, and return its IUnknown interface.
ISyncChange* pChange = NULL;
hr = pLoadChangeContext->GetSyncChange(&pChange);
if (SUCCEEDED(hr))
{
SYNC_GID gidItem;
DWORD cbID = sizeof(gidItem);
hr = pChange->GetRootItemId((BYTE*)&gidItem, &cbID);
if (SUCCEEDED(hr))
{
IXMLDOMNode* pNodeItem = NULL;
hr = FindItem(&gidItem, &pNodeItem);
if (SUCCEEDED(hr))
{
IXMLDOMNode* pNodeClone = NULL;
hr = pNodeItem->cloneNode(TRUE, &pNodeClone);
if (SUCCEEDED(hr))
{
hr = pNodeClone->QueryInterface(IID_IUnknown, (void**)ppUnkData);
pNodeClone->Release();
}
pNodeItem->Release();
}
}
pChange->Release();
}
}
return hr;
}
Próximas etapas
Agora que um provedor de sincronização foi criado, você pode criar um aplicativo para hospedar a sessão de sincronização e conectá-lo ao provedor. Para obter informações sobre como fazer isso, consulte Como criar um aplicativo de sincronização não gerenciado.
Outra etapa que você pode executar é aprimorar o provedor para tratar unidades de alteração. Para obter mais informações sobre unidades de alteração, consulte Sincronizando unidades de alteração.
Você também pode criar um repositório de metadados personalizado. Para obter mais informações sobre como tratar metadados de sincronização, consulte Gerenciando metadados para provedores padrão.
Consulte também
Referência
Interface ISyncProvider
Interface IKnowledgeSyncProvider
Interface ISynchronousNotifyingChangeApplierTarget
Interface ISynchronousDataRetriever
Estrutura ID_PARAMETERS
Interface ISynchronousNotifyingChangeApplier
Conceitos
Implementando um provedor personalizado padrão
Componentes principais do Sync Framework