SharePoint 2010: Resolving Test-SPContentDatabase Errors

One of the important steps involved in SharePoint migration: 2007 to 2010 or Farm migration within 2010 is to run Test-SPContentDatabase against the content databases from the destination farm where they are being migrated to. As outlined in the description, this check is to ensure that all custom artifacts that are being referred in the content database are installed in the farm. In other words, this is to make sure that there are no orphan objects in the database and the custom solutions referring to these artifacts are working as expected. Technically, some of these checks are achieved by cross referencing content database objects with that of configuration database objects since such customizations installed in the farm gets registered in the config db. We can see all the features installed in the farm by running the below query against the config db.

 

SELECT Name,CAST(Properties AS XML).query('data(/object/sFld[@name=''m_pathDefinition'']/text())') AS 'FeatureFolder'

FROM Objects WITH (NOLOCK)

WHERE Name LIKE 'Feature%'

The first step towards resolving these errors is to identify the location. Test-SPContentDatabase command from PowerShell console against a content database gives an output as shown below:
Category        : MissingSetupFile
**Error           : True
UpgradeBlocking : False
Message         : File [Features\Feature Folder>\...] i
                  s referenced [1] times in the database [WSS_Content_....], but is not installed on the current farm. Please
                  install any feature/solution which contains this file.
Remedy          : One or more setup files are referenced in the database [WSS_Content_....], but are not installed on the curr
                  ent farm. Please install any feature or solution which contai
                  ns these files.
**

Clearly, this information is not complete in terms of the location of the missing setup file which is crucial since a content database could be hosting hundreds or thousands of sites.

The following script can be used to consolidate all the errors into a csv file:

$WEBAPP_URL="<Url of the WebApplication>"; $webApp = Get-SPWebApplication $WEBAPP_URL; $DBErrors=@(); Write-Host "WebApplication: " $webApp.Url; $webApp.ContentDatabases|%{ $contentDBName = $_.Name; Write-Host "Content Database: "$_.Name; $errors = Test-SPContentDatabase -WebApplication $webApp -Name $contentDBName; if($errors){ $errors|%{$_|Add-Member -membertype NoteProperty -Name "Database" -Value $contentDBName;} $DBErrors += $errors; } } $DBErrors | Select-Object "Database","Category","Error","UpgradeBlocking","Message","Remedy"| Export-CSV "D:\TestContentDBErrors.csv" -NoTypeInformation

The PowerShell command yields the following categories of orphan objects and as seen in the output, these are not upgrade blocking.

  • Site Orphan

The error output will give SiteId and hence this is easy to identify using Get-SPSite –Identity <SiteId>

  • Missing Feature

The problem with identifying Missing Feature is that feature definition details are stored in the configuration database and content database will only have the feature id and properties. So you will have to rely on your memory or configuration specs to identify it based on feature id. However, you can quickly get to know the site where it is activated.

Use the Feature Admin tool in CodePlex to cleanup.

        USE [<Content Database>]

 

        SELECT * FROM Features WITH (NOLOCK)

        WHERE FeatureId='<FeatureID>'

  • Missing Web Part

Orphaned web parts can be identified using this query. You could either remove this manually from the page or through API. Once it was confirmed that these web parts are not in need, I decided to go ahead with removing them through PowerShell. I noticed that WebPart.GetType gives “Microsoft.SharePoint.WebPartPages.ErrorWebPart” and based my deletion criteria based on it.

$web = Get-SPWeb <WebUrl>;

Write-Host $web.Url;

$wpManager = $web.GetLimitedWebPartManager("<PageUrl>", "Shared")

$errorWPs=@();

 

$wpManager.WebParts|%{

if($_.GetType().ToString() -eq "Microsoft.SharePoint.WebPartPages.ErrorWebPart"){

$errorWPs += $_;

}

}

 

$errorWPs|%{

Write-Host $_.GetType();

$wpManager.DeleteWebPart($_);

}

$web.Dispose;

    USE [<Content Database>]

 

    SELECT FullUrl,DirName,LeafName,tp_ZoneID

    FROM WebParts WP WITH (NOLOCK)

    INNER JOIN AllDocs DOC WITH (NOLOCK)

    ON WP.tp_PageUrlID = DOC.Id

    INNER JOIN Webs WITH (NOLOCK)

    ON DOC.WebId = Webs.Id

    WHERE tp_WebPartTypeId='<WebPartTypeGUID>'

  • Missing Assembly

Missing assemblies are usually that of event receivers provisioned through a feature or feature stapler. All event receivers are stored in the EventReceivers table. There are stored procedures (e.g. [dbo].[proc_GetEventReceivers]) and table valued functions (e.g. TVF_EventReceivers_SiteWebHost) associated with this module that you can explore to further understand how event receiver details are retrieved and updated.

    USE [<Content Database>]

 

    SELECT * FROM EventReceivers WITH (NOLOCK)

    WHERE Assembly='<Complete Assembly Name>'

 

          The HostId and HostType will help in identifying the event receiver host.

        http://msdn.microsoft.com/en-us/library/ee394866(v=prot.13).aspx

  • Missing Setup File

Missing setup files are typically files provisioned to the site as GhostableInLibrary through a feature and are orphaned because of the feature not being installed on the farm. In my case, I even saw theme folders and files appearing in the error list which may have been provisioned in a similar fashion by SharePoint APIs. I used this query to identify the location of such orphaned files

    USE [<Content Database>]

 

    SELECT FullUrl,DirName,LeafName

    FROM AllDocs WITH (NOLOCK)

    INNER JOIN Webs WITH (NOLOCK)

    ON AllDocs.WebId = Webs.Id

    WHERE SetUpPath ='<Feature\...Path>'

 

          API Equivalent is SPFile.Properties[“vti_setuppath”]