Ein aktuelles Kundenproblem im Rahmen einer Migration von SharePoint 2010 zu SharePoint 2016 bereitete die letzten Tage Kopfzerbrechen: Nach dem Migrieren einiger Site Collections, welche diverse Inhaltstypen mit Managed Metadata Taxonomiefeldern aus dem Content Type Hub verwenden, stellt sich heraus, dass diese Felder nicht mehr funktionieren. Sobald die Elemente gespeichert werden sollen, verhindert SharePoint das mit dem Hinweis
„Da zu aktualisierende ‚SPListItem‘ wude nicht mit allen Taxonomiefeldern abgerufen.“ (sic) / „The SPListItem being updated was not retrieved with all taxonomy fields.“
Dieser Artikel zeigt, wie eine mögliche Lösung eines solchen Szenarios aussehen kann.
Es ist schon besonders gemein. Es wird unter Hochdruck migriert, der Zeitplan ist eng und die Todo-Liste noch lang. Da sind zwar alle Daten schon mit DocAve hinübergewuppt worden, alles sieht gut aus – und dann beim Testen das.
![SPLIstItem Taxonomie Fehlermeldung in der Maske](http://i2.wp.com/spmoshpit.de/wp-content/uploads/2017/12/mmfix_01.png?resize=638%2C795)
Sucht man im Netz dazu, landet man früher oder später bei diesem älteren Artikel von Tjen, der allerdings das Problem nur im Kontext von Site-Vorlagen und dem Visual Studio beleuchtet. Andere Artikel empfehlen, das Ressource Throttling in der Zentraladministration hochzudrehen, was wohl in einigen Fällen geholfen haben soll. In unserem Fall war die Lösung etwas anders.
Die Fehleranalyse
Der Ursprung der Fehlermeldung lässt sich mit einem kleinen Trick nachvollziehen. Sucht man im ULS Log, findet man im Dickicht nichts wirklich Verwertbares. Keine Unexpectets oder Errors, die einem in die richtige Richtung weisen. Schaut man sich einmal an, was im Browser passiert, wenn das Item gespeichert werden soll, kann man zuerst eine Validierung der Managed Metadaten Felder durch einen ProcessQuery Call sehen.
Da das vom Browser aufgezeichnet werden kann, schaut man einmal im Netzwerkmonitor in den Antworttext dieses Calls. Hier kann man gut nachvollziehen, wie die Fehlermeldungen vom Server zurückgegeben werden.
![Fehleranalyse kaputte Managed Metadata Felder](http://i2.wp.com/spmoshpit.de/wp-content/uploads/2017/12/mmfix_02.png?resize=1260%2C732)
Außerdem erhält man in dieser Antwort eine Correlation ID, mit der man ein Merge-SPLogFile füttern kann.
Merge-SPLogFile -Correlation <ID> -Path d:\mergelogs\error.log
In unserem Fall enthält das daraus resultierende Logfile neben dem üblichen Beifang folgende recht interessante Passage:
11/23/2017 03:54:05.95 w3wp.exe (SERVER:0x23C8) 0x1CB8 SharePoint Foundation Database adhpr Medium Entering Monitored Scope (VqueryEngine). Parent=SPRequest.GetListItemDataWithCallback2 8c8f2f9e-0f82-a0da-af65-aa9f41dd9940
11/23/2017 03:54:06.04 w3wp.exe (SERVER:0x23C8) 0x1CB8 SharePoint Foundation General xxpk Medium Unable to open Lookup list ‚{39cef420-d51e-4159-aad5-6b60c6a55228}‘.[Error was 0x81020026] 8c8f2f9e-0f82-a0da-af65-aa9f41dd9940
11/23/2017 03:54:06.10 w3wp.exe (SERVER:0x23C8) 0x1CB8 SharePoint Foundation General xxpk Medium Unable to open Lookup list ‚{39cef420-d51e-4159-aad5-6b60c6a55228}‘.[Error was 0x81020026] 8c8f2f9e-0f82-a0da-af65-aa9f41dd9940
11/23/2017 03:54:06.38 w3wp.exe (SERVER:0x23C8) 0x1CB8 SharePoint Foundation Database asutt High [Forced
11/23/2017 03:54:06.38 w3wp.exe (SERVER:0x23C8) 0x1CB8 SharePoint Foundation Database ax0rv High Leaving Monitored Scope: (SPSqlClient(proc_GetListMetaDataAndEventReceivers)) Execution Time=272.248847269695; CPU Milliseconds=0; SQL Query Count=0; Parent=VqueryEngine 8c8f2f9e-0f82-a0da-af65-aa9f41dd9940
Die Validierung der Felder schlägt also fehl, weil eine Liste nicht gefunden werden konnte. Wenn man nun weiß, wie die Anzeige von Taxonomiefeldern in SharePoint funktioniert, kann man sich den Rest zusammenreimen. Denn es existiert in jeder SiteCollection auf der Root-Webseite eine versteckte Liste, welche die Anzeigenamen sowie diverse weitere Informationen enthält. Wenn man im Browser hinter der Adresse einer Site Collection noch ein „/lists/TaxonomyHiddenList“ anfügt, kann man diese sichtbar machen.
![TaxonomyHiddenList](http://i2.wp.com/spmoshpit.de/wp-content/uploads/2017/12/mmfix_03.png?resize=1115%2C860)
Felder mit verwalteten Metadaten bestehen selber aus zwei verschiedenen Feldern: Einmal wird mit dem Anzeigetext in einem Feld eine sichtbare Information gespeichert. Das ist der Text, der einem angezeigt wird, wenn man ein solches Feld angezeigt bekommt. Daneben gibt es in der Liste/Bibliothek noch ein Feld, das sämtliche technischen Informationen enthält, und dieses ist ein Lookup auf die genannte Liste in der Root-Webseite einer Site Collection. Der Verweis findet also nicht direkt auf den Term Store der Managed Metadata Service Application statt sondern nimmt den Umweg über einen lokal geführten Index. Hier werden neben den Verweisen auf den Term Store auch für diverse Sprachen die Anzeigewerte hinterlegt.
![TaxonomyHiddenList](http://i1.wp.com/spmoshpit.de/wp-content/uploads/2017/12/mmfix_04.png?resize=720%2C725)
Die Liste, die im vorliegenden Fall nun also nicht gefunden wird, ist also genau dieser Lookup. Das bedeutet, dass die kaputten Felder in der Liste eine falsche Listen-Id als Referenz in ihrem Schema gespeichert haben. Wie die in unserem Fall dorthin gekommen ist, ist eine spannende Frage. Offensichtlich hat hier etwas mit dem Content Type Hub und dem Anlegen der Liste bei der Generierung der Site Collection während der Migration nicht funktioniert.
Die Reparatur der Managed Metadata Felder
Letztlich muss man nun sicherstellen, dass die Referenz wieder stimmt. Dazu braucht man die Id der versteckten Liste und die Id des Rootwebs. Vergleicht man diese mit den Werten, die im Schema des Felds stehen und die beiden Paare stimmen nicht überein, kann man das beschriebene Fehlerverhalten sehen. So ein Unterschied zwischen einer funktionierenden und einer kaputten Felddefinition könnte z.B. so aussehen:
<Field Type="TaxonomyFieldType" DisplayName="Firma" List="{9d4fd7bc-2dfe-4cbf-80a7-b2341350aba6}" WebId="161df174-89bc-405d-a5c4-de49c7e2f5ff" ShowField="Term1031" Required="TRUE" EnforceUniqueValues="FALSE" Group="..." ID="..." ></Field> <Field Type="TaxonomyFieldType" DisplayName="Firma" List="{68afc1e1-d789-42ea-b74e-b67344cb06d2}" WebId="161df174-89bc-405d-a5c4-de49c7e2f5ff" ShowField="Term1031" Required="TRUE" EnforceUniqueValues="FALSE" Group="..." ID="..." ></Field>
Eine Lösung ist nun, diese Felder neu anzulegen, eine andere, das SchemaXml mit den richtigen Ids neu zu beschreiben. Das könnte man mit PowerShell realisieren:
$web = Get-SPWeb $webUrl $list = $web.Lists[$listname] $field = $list.Fields[$fieldname] $newSchema = $field.SchemaXml if ($field.LookupList -ne $newListId) { $newSchema = $newSchema -replace $currentListId, $newListId } if ($field.LookupWebId -ne $newwebID) { $newSchema = $newSchema -replace $currentWebId, $newwebID } $field.SchemaXml = $newSchema $field.Update()
Verpackt man das in ein Skript und legt die Vermutung zugrunde, dass bei allen Feldern, bei denen die Ids nicht mit denen der versteckten Liste übereinstimmen, betroffen sind, kann man ein Analyseskript schreiben. Zum Reparieren spendiert man man eine Autofix-Option. Ruft man es einfach so auf, geht es alle Webs der angegebenen Site Collections rekursiv durch und durchsucht vorhandene Listen und Bibliotheken nach dem beschriebenen Schema. Gefundene Felder werden in einer CSV-Logdatei mit allen notwendigen Informationen angelegt. Setzt man den Autofix-Parameter auf $true, werden die betreffenden Felder repariert.
Damit man das Skript auch für eine größere Anzahl an Site Collections verwenden kann, ist es mit einem CSV-Input ausgestattet. Als Eingabe wird also eine Datei verwendet, in der die vollen URLs stehen und „Url“ als Spaltentitel erwartet wird.
<# .SYNOPSIS Checks the taxonomy fields in lists on webs in given site collections if there are faulty entries pointing not to the TaxonomyHiddenLists in the root web. .DESCRIPTION Checks the taxonomy fields in lists on webs in given site collections if there are faulty entries pointing not to the TaxonomyHiddenLists in the root web. Can autofix these fields by exchanging the IDs in the SchemaXml of these fields. .PARAMETER csvFile The name of the csv file. .PARAMETER autofix If set to $true, the script will repair broken fields by exchanging appropiate ListID and WebId attributes in the SchemaXml. .EXAMPLE .\Check-TaxonomyFieldReferences.ps1 -csvFile webs.csv -autofix $true .NOTES Author: Carsten Büttemeier Date: 25.11.2017 #> [CmdletBinding()] Param( [Parameter(Mandatory = $True, Position = 1)] [string]$csvFile, [Parameter(Mandatory = $false, Position = 2)] [bool]$autofix = $false ) #----SnapIns----------------------------------------- if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell") -eq $null) { Add-PSSnapin Microsoft.SharePoint.PowerShell } #----Variables---------------------------------------- #set logging files $x = (Split-Path -Parent $MyInvocation.MyCommand.Definition) + "\CheckTaxonomyReferences.log" ac $x ("WebUrl, ListId, ExpectedListId, WebId, ExpectedWebId") #----Execute----------------------------------------- function FixField() { Param( [Parameter (Mandatory = $true)] [string]$webUrl, [Parameter (Mandatory = $true)] [string]$listname, [Parameter (Mandatory = $true)] [string]$fieldname, [Parameter (Mandatory = $true)] [string]$currentListId, [Parameter (Mandatory = $true)] [string]$newListId, [Parameter (Mandatory = $true)] [string]$currentWebId, [Parameter (Mandatory = $true)] [string]$newwebID ) # Fixing the taxonomy list lookup if ($autofix -eq $true) { Write-Host "Fixing column.." -ForegroundColor Yellow -NoNewline $web = Get-SPWeb $webUrl $list = $web.Lists[$listname] $field = $list.Fields[$fieldname] $newSchema = $field.SchemaXml if ($field.LookupList -ne $newListId) { $newSchema = $newSchema -replace $currentListId, $newListId } if ($field.LookupWebId -ne $newwebID) { $newSchema = $newSchema -replace $currentWebId, $newwebID } $field.SchemaXml = $newSchema $field.Update() Write-Host "..done." -ForegroundColor Green -NoNewline } } function CheckWeb() { Param( [Parameter (Mandatory = $true)] [string]$webUrl ) $brokenFields = @() Write-Host "Checking web with URL " -ForegroundColor Cyan -NoNewline Write-Host $webUrl -ForegroundColor Gray $web = Get-SPWeb -Identity $webUrl -ErrorAction SilentlyContinue if ($web) { Write-Host "..Web Id " -ForegroundColor Cyan -NoNewline Write-Host $web.ID -ForegroundColor Gray $taxonomyHiddenList = $web.Site.RootWeb.Lists["TaxonomyHiddenList"] Write-Host "..TaxonomyHiddenList Id " -ForegroundColor Cyan -NoNewline Write-Host $taxonomyHiddenList.ID -ForegroundColor Gray Write-Host "..Checking Lists and Libraries " -ForegroundColor Cyan foreach ($list in $web.Lists) { $listUrl = $list.RootFolder.ServerRelativeUrl Write-Host "..checking list $listUrl " -ForegroundColor Gray -NoNewline $fields = $list.Fields | Where {$_.TypeAsString -eq "TaxonomyFieldType" -or $_.TypeAsString -eq "TaxonomyFieldTypeMulti"} if (!$fields) { Write-Host "..no taxonomy fields in list." -ForegroundColor Cyan } else { foreach ($f in $fields) { $broken = $false $expectedListId = "{$($taxonomyHiddenList.ID)}" $expectedWebId = $web.Site.RootWeb.Id Write-Host "..checking field $($f.Title) " -ForegroundColor Gray -NoNewline if ($f.LookupList -ne $expectedListId) { $broken = $true Write-Host "..ListId mismatch.." -ForegroundColor Yellow -NoNewline } else { Write-Host "..ListId ok.." -ForegroundColor Green -NoNewline } if ($f.LookupWebId -ne $expectedWebId) { $broken = $true Write-Host "..WebId mismatch.." -ForegroundColor Yellow } else { Write-Host "..WebId ok.." -ForegroundColor Green } if ($broken) { $listId = $f.LookupList $webId = $f.LookupWebId ac $x ("$listUrl, $listId, $expectedListId, $webId, $expectedWebId") if ($autofix -eq $true) { # collect data to fix this outside the foreach loop. $fieldObject = New-Object PSCustomObject $fieldObject | Add-Member -type NoteProperty -name WebUrl -Value $webUrl $fieldObject | Add-Member -type NoteProperty -name ListName -Value $list.Title $fieldObject | Add-Member -type NoteProperty -name FieldName -Value $f.Title $fieldObject | Add-Member -type NoteProperty -name CurrentListId -Value $listId $fieldObject | Add-Member -type NoteProperty -name NewListId -Value $expectedListId $fieldObject | Add-Member -type NoteProperty -name CurrentWebId -Value $webId $fieldObject | Add-Member -type NoteProperty -name NewWebId -Value $expectedWebId $brokenFields += $fieldObject } } } } } foreach ($broken in $brokenFields) { FixField -webUrl $broken.WebUrl -listname $broken.ListName -fieldname $broken.FieldName -newListId $broken.NewListId -currentListId $broken.CurrentListId -currentWebId $broken.CurrentWebId -newwebID $broken.NewWebId } } foreach ($subWeb in $web.Webs) { $url = $subWeb.Url CheckWeb -webUrl $url } } try { #get the input csv $csvImport = Import-Csv ((Split-Path -Parent $MyInvocation.MyCommand.Definition) + "\$csvFile") $numberOfWebs = $csvImport.count Write-Host "$numberOfWebs webs found in file $csvFile" -ForegroundColor Gray } catch { Write-Host "File $csvFile could not be found or error while importing csv" -ForegroundColor Yellow } # Check all given site collections for ($i = 0; $i -lt $csvImport.count; $i++) { Write-Host "Checking broken Taxonomy lookups in list definitions in site " -ForegroundColor Cyan -NoNewline Write-Host $csvImport[$i].URL -ForegroundColor Yellow $saWeb = Get-SPWeb -Identity $csvImport[$i].URL -ErrorAction SilentlyContinue if ($saWeb) { CheckWeb -webUrl $csvImport[$i].URL } else { Write-Host "Site doesn't exist" } $saWeb.Dispose() }
Wie immer gilt dabei, Skripte aus dem Internet nicht ohne Prüfung zu verwenden und erst recht nicht auf Livedaten ungetestet anzuwenden! Ich übernehme keine Gewähr, die Verwendung erfolgt auf eigene Gefahr.
Nichtsdestotrotz hoffe ich, dass das Skript bei ähnlichen Fällen helfen kann und würde mich über Rückmeldungen dazu freuen.
Der Beitrag Referenzen in SharePoint Managed Metadata Feldern mit PowerShell reparieren erschien zuerst auf SharePoint Moshpit.