Partilhar via


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.

Screenshot that shows the SharePoint admin center, with the term store, the taxonomy term store search box, and the term store administrators boxes highlighted.

Atribuir permissões de repositório de termos:

  1. No Centro de administração do Office 365, escolha repositório de termos.

  2. No repositório de taxonomia, escolha a definição de termo que você deseja atribuir a um administrador.

  3. 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.

Screenshot of the console application prompting for information to be entered.

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.

Screenshot of the taxonomy term store drop-down list.

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:

  1. Recupera os objetos de armazenamento de termo de origem e de destino.

  2. Verifica se os idiomas dos armazenamentos de termo de origem e de destino correspondem.

  3. 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 &amp;&amp; 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 &amp;&amp; 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

Confira também