Sincronizar grupos de termo exemplo suplemento do SharePoint
O exemplo Core.MMSSync mostra como usar um suplemento hospedado pelo provedor para sincronizar uma taxonomia de origem e destino. Esse suplemento sincroniza dois armazenamentos de termo no serviço de metadados gerenciados: uma origem e um armazenamento de termos de destino.
Os seguintes objetos são usados para sincronizar grupos de termos:
- TermStore
- ChangeInformation
Use esta solução se desejar:
- Sincronizar duas taxonomias. Por exemplo, você pode usar o SharePoint Online e o SharePoint Server local para diferentes conjuntos de dados, mas eles usam a mesma taxonomia.
- Sincronizar alterações feitas somente em um grupo de termo específico.
Antes de começar
Para começar, baixe o suplemento de exemplo Core.MMSSync do projeto Office 365 Padrões e Práticas do Desenvolvedor no GitHub.
Observação
The code in this article is provided as-is, without warranty of any kind, either express or implied, including any implied warranties of fitness for a particular purpose, merchantability, or non-infringement.
Antes de executar esse suplemento, você precisará de permissão para acessar o armazenamento de termos no serviço de metadados gerenciados. A figura a seguir mostra o Centro de administração do Office 365 onde são atribuídas as permissões.
Atribuir permissões de repositório de termos:
No Centro de administração do Office 365, escolha repositório de termos.
No repositório de taxonomia, escolha a definição de termo que você deseja atribuir a um administrador.
Em Administrador do Repositório de Termos, insira a conta organizacional que exige permissões de administrador do repositório de termos.
Usando o suplemento de exemplo Core.MMSSync
Ao iniciar o suplemento, você verá um aplicativo de console Core.MMSSync, conforme mostrado na próxima figura. Você será solicitado a inserir as seguintes informações:
- A URL do centro de administração Office 365 que contém o armazenamento de termo de origem (esta é a URL do serviço de metadados gerenciados de origem). Por exemplo, você pode inserir
https://contososource-admin.sharepoint.com
. - O nome de usuário e a senha de um administrador de armazenamento de termo em seu serviço de metadados gerenciados de origem.
- A URL do centro de administração Office 365 que contém o armazenamento de termos de destino (esta é a URL do MMS de destino). Por exemplo, você pode inserir
https://contosotarget-admin.sharepoint.com
. - O nome de usuário e a senha de um administrador de armazenamento de termos em seu serviço de metadados gerenciados de destino.
- O tipo de operação que você deseja executar. Você pode:
- Mova um grupo de termos (Cenário 1) usando o objeto TermStore .
- Processar alterações (Cenário 2) usando o objeto ChangeInformation .
Importante
Esse suplemento de exemplo funciona com o SharePoint Online e o SharePoint Server local.
Depois de selecionar seu cenário, insira o nome do grupo de termo que deseja sincronizar de sua origem para o serviço de metadados gerenciados de destino, conforme mostrado na figura a seguir. Por exemplo, você pode inserir Enterprise
.
Cenário 1 – Mover grupo de termos
Quando você seleciona Mover Grupo de Termos, o suplemento solicita que você insira um grupo de termos para sincronizar e, em seguida, chama o método CopyNewTermGroups em MMSSyncManager.cs. CopyNewTermGroups então faz o seguinte para copiar um grupo de termos do repositório de termo de origem para o armazenamento de termos de destino:
Recupera os objetos de armazenamento de termo de origem e de destino.
Verifica se os idiomas dos armazenamentos de termo de origem e de destino correspondem.
Verifica se o grupo de termo de origem não existe no repositório de termos de destino e copia o grupo de termo de origem para o armazenamento de termos de destino usando CreateNewTargetTermGroup.
Você pode definir os parâmetros TermGroupExclusions, TermGroupToCopy e TermSetInclusions para filtrar quais termos são processados.
O código a seguir mostra os métodos CopyNewTermGroups e CreateNewTargetTermGroup em MMSSyncManager.cs.
public bool CopyNewTermGroups(ClientContext sourceContext, ClientContext targetContext, List<string> termGroupExclusions = null, string termGroupToCopy = null)
{
TermStore sourceTermStore = GetTermStoreObject(sourceContext);
TermStore targetTermStore = GetTermStoreObject(targetContext);
List<int> languagesToProcess = null;
if (!ValidTermStoreLanguages(sourceTermStore, targetTermStore, out languagesToProcess))
{
Log.Internal.TraceError((int)EventId.LanguageMismatch, "The target termstore default language is not available as language in the source term store, syncing cannot proceed.");
return false;
}
// Get a list of term groups to process. Exclude site collection-scoped groups and system groups.
IEnumerable<TermGroup> termGroups = sourceContext.LoadQuery(sourceTermStore.Groups.Include(g => g.Name,
g => g.Id,
g => g.IsSiteCollectionGroup,
g => g.IsSystemGroup))
.Where(g => g.IsSystemGroup == false && g.IsSiteCollectionGroup == false);
sourceContext.ExecuteQuery();
foreach (TermGroup termGroup in termGroups)
{
// Skip term group if you only want to copy one particular term group.
if (!String.IsNullOrEmpty(termGroupToCopy))
{
if (!termGroup.Name.Equals(termGroupToCopy, StringComparison.InvariantCultureIgnoreCase))
{
continue;
}
}
// Skip term groups that you do not want to copy.
if (termGroupExclusions != null && termGroupExclusions.Contains(termGroup.Name, StringComparer.InvariantCultureIgnoreCase))
{
Log.Internal.TraceInformation((int)EventId.CopyTermGroup_Skip, "Skipping {0} as this is a system termgroup", termGroup.Name);
continue;
}
// About to start copying a term group.
TermGroup sourceTermGroup = GetTermGroup(sourceContext, sourceTermStore, termGroup.Name);
TermGroup targetTermGroup = GetTermGroup(targetContext, targetTermStore, termGroup.Name);
if (sourceTermGroup == null)
{
continue;
}
if (targetTermGroup != null)
{
if (sourceTermGroup.Id != targetTermGroup.Id)
{
// Term group exists with a different ID, unable to sync.
Log.Internal.TraceWarning((int)EventId.CopyTermGroup_IDMismatch, "The term groups have different ID's. I don't know how to work it.");
}
else
{
// Do nothing as this term group was previously copied. Term group changes need to be
// picked up by the change log processing.
Log.Internal.TraceInformation((int)EventId.CopyTermGroup_AlreadyCopied, "Termgroup {0} was already copied...changes to it will need to come from changelog processing.", termGroup.Name);
}
}
else
{
Log.Internal.TraceInformation((int)EventId.CopyTermGroup_Copying, "Copying termgroup {0}...", termGroup.Name);
this.CreateNewTargetTermGroup(sourceContext, targetContext, sourceTermGroup, targetTermStore, languagesToProcess);
}
}
return true;
}
private void CreateNewTargetTermGroup(ClientContext sourceClientContext, ClientContext targetClientContext, TermGroup sourceTermGroup, TermStore targetTermStore, List<int> languagesToProcess)
{
TermGroup destinationTermGroup = targetTermStore.CreateGroup(sourceTermGroup.Name, sourceTermGroup.Id);
if (!string.IsNullOrEmpty(sourceTermGroup.Description))
{
destinationTermGroup.Description = sourceTermGroup.Description;
}
TermSetCollection sourceTermSetCollection = sourceTermGroup.TermSets;
if (sourceTermSetCollection.Count > 0)
{
foreach (TermSet sourceTermSet in sourceTermSetCollection)
{
sourceClientContext.Load(sourceTermSet,
set => set.Name,
set => set.Description,
set => set.Id,
set => set.Contact,
set => set.CustomProperties,
set => set.IsAvailableForTagging,
set => set.IsOpenForTermCreation,
set => set.CustomProperties,
set => set.Terms.Include(
term => term.Name,
term => term.Description,
term => term.Id,
term => term.IsAvailableForTagging,
term => term.LocalCustomProperties,
term => term.CustomProperties,
term => term.IsDeprecated,
term => term.Labels.Include(label => label.Value, label => label.Language, label => label.IsDefaultForLanguage)));
sourceClientContext.ExecuteQuery();
TermSet targetTermSet = destinationTermGroup.CreateTermSet(sourceTermSet.Name, sourceTermSet.Id, targetTermStore.DefaultLanguage);
targetClientContext.Load(targetTermSet, set => set.CustomProperties);
targetClientContext.ExecuteQuery();
UpdateTermSet(sourceClientContext, targetClientContext, sourceTermSet, targetTermSet);
foreach (Term sourceTerm in sourceTermSet.Terms)
{
Term reusedTerm = targetTermStore.GetTerm(sourceTerm.Id);
targetClientContext.Load(reusedTerm);
targetClientContext.ExecuteQuery();
Term targetTerm;
if (reusedTerm.ServerObjectIsNull.Value)
{
try
{
targetTerm = targetTermSet.CreateTerm(sourceTerm.Name, targetTermStore.DefaultLanguage, sourceTerm.Id);
targetClientContext.Load(targetTerm, term => term.IsDeprecated,
term => term.CustomProperties,
term => term.LocalCustomProperties);
targetClientContext.ExecuteQuery();
UpdateTerm(sourceClientContext, targetClientContext, sourceTerm, targetTerm, languagesToProcess);
}
catch (ServerException ex)
{
if (ex.Message.IndexOf("Failed to read from or write to database. Refresh and try again.") > -1)
{
// This exception was due to caching issues and generally is thrown when terms are reused across groups.
targetTerm = targetTermSet.ReuseTerm(reusedTerm, false);
}
else
{
throw ex;
}
}
}
else
{
targetTerm = targetTermSet.ReuseTerm(reusedTerm, false);
}
targetClientContext.Load(targetTerm);
targetClientContext.ExecuteQuery();
targetTermStore.UpdateCache();
// Refresh session and term store references to force reload of the term just added. You need
// to do this because there can be an update change event following next, and if you don't,
// the newly created term set cannot be obtained from the server.
targetTermStore = GetTermStoreObject(targetClientContext);
// Recursively add the other terms.
ProcessSubTerms(sourceClientContext, targetClientContext, targetTermSet, targetTerm, sourceTerm, languagesToProcess, targetTermStore.DefaultLanguage);
}
}
}
targetClientContext.ExecuteQuery();
}
Cenário 2 – Alterações no processo
Quando você seleciona Alterações de Processo, o suplemento solicita que você insira um Grupo de Termos para sincronizar e, em seguida, chama o método ProcessChanges em MMSSyncManager.cs. ProcessChanges usa o método GetChanges da classe ChangedInformation para recuperar todas as alterações feitas em grupos, conjuntos de termos e termos no serviço de metadados gerenciados de origem. Em seguida, as alterações são aplicadas ao serviço de metadados gerenciados de destino.
Observação
Este documento inclui apenas algumas partes do método ProcessChanges . Para examinar todo o método, abra a solução Core.MMSSync no Visual Studio.
O método ProcessChanges começa criando um objeto TaxonomySession .
Log.Internal.TraceInformation((int)EventId.TaxonomySession_Open, "Opening the taxonomy session");
TaxonomySession sourceTaxonomySession = TaxonomySession.GetTaxonomySession(sourceClientContext);
TermStore sourceTermStore = sourceTaxonomySession.GetDefaultKeywordsTermStore();
sourceClientContext.Load(sourceTermStore,
store => store.Name,
store => store.DefaultLanguage,
store => store.Languages,
store => store.Groups.Include(group => group.Name, group => group.Id));
sourceClientContext.ExecuteQuery();
Em seguida, ele recupera alterações usando o objeto ChangeInformation e definindo a data de início no objeto ChangeInformation . Este exemplo recupera todas as alterações feitas no último ano.
Log.Internal.TraceInformation((int)EventId.TermStore_GetChangeLog, "Reading the changes");
ChangeInformation changeInformation = new ChangeInformation(sourceClientContext);
changeInformation.StartTime = startFrom;
ChangedItemCollection termStoreChanges = sourceTermStore.GetChanges(changeInformation);
sourceClientContext.Load(termStoreChanges);
sourceClientContext.ExecuteQuery();
O método GetChanges retorna um ChangedItemCollection, que enumera todas as alterações que ocorrem no armazenamento de termos, conforme mostrado no exemplo de código a seguir. A última linha do exemplo verifica se o ChangedItem era um grupo de termos. ProcessChanges inclui código para executar verificações semelhantes no ChangedItem para conjuntos de termos e termos.
foreach (ChangedItem _changeItem in termStoreChanges)
{
if (_changeItem.ChangedTime < startFrom)
{
Log.Internal.TraceVerbose((int)EventId.TermStore_SkipChangeLogEntry, "Skipping item {1} changed at {0}", _changeItem.ChangedTime, _changeItem.Id);
continue;
}
Log.Internal.TraceVerbose((int)EventId.TermStore_ProcessChangeLogEntry, "Processing item {1} changed at {0}. Operation = {2}, ItemType = {3}", _changeItem.ChangedTime, _changeItem.Id, _changeItem.Operation, _changeItem.ItemType);
#region Group changes
if (_changeItem.ItemType == ChangedItemType.Group)
O tipo de item alterado pode ser um grupo de termos, um conjunto de termos ou um termo. Cada tipo de item alterado tem operações diferentes que você pode executar nele. A tabela a seguir lista as operações que você pode executar em cada tipo de item alterado.
O que mudou? (ChangedItemType) | Operações que você pode executar no tipo de item alterado (ChangedOperationType) |
---|---|
Group | Excluir grupo Adicionar grupo Editar grupo |
Conjunto de Termos | Excluir conjunto de termos Mover conjunto de termos Copiar conjunto de termos Adicionar conjunto de termos Editar conjunto de termos |
Termo | Excluir termo Mover termo Copiar termo Termo de alteração de caminho Termo merge Adicionar termo Editar termo |
O código a seguir mostra como executar uma operação de exclusão quando um grupo de termos foi excluído no serviço de metadados gerenciados pela origem.
#region Delete group
if (_changeItem.Operation == ChangedOperationType.DeleteObject)
{
TermGroup targetTermGroup = targetTermStore.GetGroup(_changeItem.Id);
targetClientContext.Load(targetTermGroup, group => group.Name);
targetClientContext.ExecuteQuery();
if (!targetTermGroup.ServerObjectIsNull.Value)
{
if (termGroupExclusions == null || !termGroupExclusions.Contains(targetTermGroup.Name, StringComparer.InvariantCultureIgnoreCase))
{
Log.Internal.TraceInformation((int)EventId.TermGroup_Delete, "Deleting group: {0}", targetTermGroup.Name);
targetTermGroup.DeleteObject();
targetClientContext.ExecuteQuery();
}
}
}
#endregion