Freigeben über


Beispiel für Das SharePoint-Add-In zum Synchronisieren von Begriffsgruppen

Das Core.MMSSync-Beispiel zeigt, wie Sie ein vom Anbieter gehostetes Add-In verwenden, um eine Quell- und Zieltaxonomie zu synchronisieren. Dieses Add-In synchronisiert zwei Terminologiespeicher im verwalteten Metadatendienst– einen Quell- und einen Zielausdrucksspeicher.

Die folgenden Objekte werden zum Synchronisieren von Begriffsgruppen verwendet:

  • Termstore
  • ChangeInformation

Verwenden Sie diese Lösung, wenn Folgendes auf Sie zutrifft:

  • Synchronisieren sie zwei Taxonomien. Beispielsweise können Sie sowohl SharePoint Online als auch SharePoint Server lokal für unterschiedliche Datensätze verwenden, aber sie verwenden dieselbe Taxonomie.
  • Synchronisieren Sie nur Änderungen, die an einer bestimmten Ausdrucksgruppe vorgenommen wurden.

Bevor Sie beginnen

Laden Sie zunächst das Beispiel-Add-In Core.MMSSync aus dem Projekt Office 365 Developer Patterns and Practices auf GitHub herunter.

Hinweis

Der Code in diesem Artikel wird wie besehen und ohne jegliche Garantie zur Verfügung gestellt, gleich ob ausdrücklich oder konkludent, einschließlich jedweder stillschweigenden Gewährleistung der Eignung für einen bestimmten Zweck, Marktgängigkeit oder Nichtverletzung von Rechten.

Bevor Sie dieses Add-In ausführen, benötigen Sie die Berechtigung für den Zugriff auf den Terminologiespeicher im verwalteten Metadatendienst. Die nachstehende Abbildung zeigt das Office 365 Admin Center, dem diese Berechtigungen zugewiesen werden.

Screenshot, des SharePoint Admin Centers mit hervorgehobenen Terminologiespeicher, Taxonomieterminologiespeicher-Suchfeld, und Terminologiespeicheradministratorfeldern.

So weisen Sie dem Terminologiespeicher Berechtigungen zu:

  1. Wählen Sie im Office 365 Admin Center Terminologiespeicher aus.

  2. Wählen Sie in TAXONOMIETERMINOLOGIESPEICHER den Ausdruckssatz aus, den Sie einem Administrator zuweisen möchten.

  3. Geben Sie in Terminologiespeicheradministratoren das Organisationskonto ein, bei dem Berechtigungen für den Terminologiespeicheradministrator erforderlich sind.

Verwenden des Core.MMSSync-Beispiel-Add-Ins

Wenn Sie das Add-In starten, wird eine Core.MMSSync-Konsolenanwendung angezeigt, wie in der nächsten Abbildung dargestellt. Sie werden aufgefordert, die folgenden Informationen einzugeben:

  • Die URL des Office 365 Admin Center, das den Quellbegriffsspeicher enthält (dies ist die URL des quellverwalteten Metadatendiensts). Sie können z. B. eingeben https://contososource-admin.sharepoint.com.
  • Der Benutzername und das Kennwort eines Terminologiespeicheradministrators in Ihrem verwalteten Quellmetadatendienst.
  • Die URL des Office 365 Admin Center, das den Zielbegriffsspeicher enthält (dies ist die URL des Ziel-MMS). Sie können z. B. eingeben https://contosotarget-admin.sharepoint.com.
  • Der Benutzername und das Kennwort eines Terminologiespeicheradministrators für Ihren verwalteten Zielmetadatendienst.
  • Der Typ des Vorgangs, den Sie ausführen möchten. Sie können eine der folgenden Aktionen ausführen:
    • Verschieben einer Begriffsgruppe (Szenario 1) mithilfe des TermStore-Objekts .
    • Verarbeiten von Änderungen (Szenario 2) mithilfe des ChangeInformation-Objekts .

Wichtig

Dieses Beispiel-Add-In funktioniert sowohl mit SharePoint Online als auch mit SharePoint Server lokal.

Screenshot der Konsolenanwendung, die dazu auffordert, Informationen einzugeben.

Nachdem Sie Ihr Szenario ausgewählt haben, geben Sie den Namen der Begriffsgruppe ein, die Sie von Ihrer Quelle mit Ihrem verwalteten Zielmetadatendienst synchronisieren möchten, wie in der folgenden Abbildung dargestellt. Sie können z. B. eingeben Enterprise.

Screenshot der Taxonomieterminologiespeicher-Dropdownliste.

Szenario 1: Verschieben der Begriffsgruppe

Wenn Sie Ausdrucksgruppe verschieben auswählen, werden Sie vom Add-In aufgefordert, eine Zu synchronisierende Ausdrucksgruppe einzugeben, und ruft dann die CopyNewTermGroups-Methode in MMSSyncManager.cs auf. CopyNewTermGroups führt dann die folgenden Schritte aus, um eine Ausdrucksgruppe aus dem Quellbegriffsspeicher in den Zielbegriffsspeicher zu kopieren:

  1. Ruft die Quell- und Zielbegriffsspeicherobjekte ab.

  2. Überprüft, ob die Sprachen der Quell- und Zielausdrucksspeicher übereinstimmen.

  3. Überprüft, ob die Quellbegriffsgruppe im Zielbegriffsspeicher nicht vorhanden ist, und kopiert dann die Quellbegriffsgruppe mithilfe von CreateNewTargetTermGroup in den Zielbegriffsspeicher.

Sie können die Parameter TermGroupExclusions, TermGroupToCopy und TermSetInclusions festlegen, um zu filtern, welche Begriffe verarbeitet werden.

Der folgende Code zeigt die Methoden CopyNewTermGroups und CreateNewTargetTermGroup in 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();
        }

Szenario 2: Verarbeiten von Änderungen

Wenn Sie Änderungen verarbeiten auswählen, werden Sie vom Add-In aufgefordert, eine zu synchronisierende Ausdrucksgruppe einzugeben, und ruft dann die ProcessChanges-Methode in MMSSyncManager.cs auf. ProcessChanges verwendet die GetChanges-Methode der ChangedInformation-Klasse , um alle Änderungen abzurufen, die an Gruppen, Ausdruckssätzen und Begriffen im verwalteten Metadatendienst der Quelle vorgenommen wurden. Änderungen werden dann auf den verwalteten Metadatendienst des Ziels angewendet.

Hinweis

Dieses Dokument enthält nur einige Teile der ProcessChanges-Methode . Um die gesamte Methode zu überprüfen, öffnen Sie die Projektmappe Core.MMSSync in Visual Studio.


Die ProcessChanges-Methode erstellt zunächst ein TaxonomySession-Objekt .

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();


Als Nächstes werden Änderungen mithilfe des ChangeInformation-Objekts abgerufen und das Startdatum für das ChangeInformation-Objekt festgelegt. In diesem Beispiel werden alle Änderungen abgerufen, die im letzten Jahr vorgenommen wurden.

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();


Die GetChanges-Methode gibt eine ChangedItemCollection zurück, die alle Änderungen aufzählt, die im Ausdrucksspeicher auftreten, wie im folgenden Codebeispiel gezeigt. Die letzte Zeile des Beispiels überprüft, ob das ChangedItem eine Ausdrucksgruppe war. ProcessChanges enthält Code zum Ausführen ähnlicher Überprüfungen des ChangedItem für Ausdruckssätze und Ausdrücke.

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)


Der geänderte Elementtyp kann eine Ausdrucksgruppe, ein Ausdruckssatz oder ein Ausdruck sein. Jeder geänderte Elementtyp verfügt über unterschiedliche Vorgänge, die Sie für ihn ausführen können. In der folgenden Tabelle sind die Vorgänge aufgeführt, die Sie für jeden geänderten Elementtyp ausführen können.


Was hat sich geändert? (ChangedItemType) Vorgänge, die Sie für einen geänderten Elementtyp ausführen können (ChangedOperationType)
Gruppe

Gruppe löschen

Gruppe hinzufügen

Gruppe bearbeiten

TermSet

Ausdruckssatz löschen

Ausdruckssatz verschieben

Ausdruckssatz kopieren

Ausdruckssatz hinzufügen

Ausdruckssatz bearbeiten

Begriff

Begriff löschen

Begriff verschieben

Ausdruck kopieren

Pfadänderungsbegriff

Ausdruck zusammenführen

Ausdruck hinzufügen

Ausdruck bearbeiten

Der folgende Code zeigt, wie sie einen Löschvorgang ausführen, wenn eine Begriffsgruppe im quellverwalteten Metadatendienst gelöscht wurde.

#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

Siehe auch