29 czerwca 2011

Test-SPContentDatabase and how to locate MissingWebPart

MissingWebPart error

During preparation phase for SharePoint 2010 upgrade from WSS/MOSS 2007, often Test-SPContentDatabase command is being executed against existing SharePoint 2007 content databases.

Test-SPContentDatabase -Name ContenDatabaseName -WebApp WebApplicationUrl > TestPreUpgradeLog.txt

One of the error types (not blocking the upgrade though) that could be found in Test-SPContentDatabase execution log (TestPreUpgradeLog.txt in the sample above) is called MissingWebPart. Error description from the pre-upgrade log is shown below:

Category        : MissingWebPart
Error           : True
UpgradeBlocking : False
Message         : WebPart class [WebPartTypeId here] is referenced [x] times in the database [ContentDatabase name here], but is not installed on the current farm. Please install any feature/solution which contains this web part. Remedy : One or more web parts are referenced in the database [ContentDatabase name here], but are not installed on the current farm. Please install any feature or solution which contains these web parts.

WebPartTypeId cannot be reverse-engineered

If there is a large number of site collections in the content database that could contain MissingWebPart references, it's not easy to locate pages that are hosting webpart instance with WebPartTypeId provided in the pre-upgrade log. The reason for MissingWebPart is missing assembly in the GAC that defines the webpart type or missing SafeControl entry in  theWeb.config for particular webpart.

WebPartTypeId is calculated from the full qualified assembly name and the full webpart type name. On codeplex.com there is a tool called  easywebparttypeidgen that can calculate WebPartTypeId.

Why would you ever want to calculate WebPartTypeId? Because, WebPartTypeId cannot be reverse-engineered, as it is a hash-value that was calculated once and forever. So, until you will GUESS the webpart type you won't know what is behind the cryptic WebPartTypeId. The value of WebPartTypeId will change if assembly version, web part type name or public key token will be modified.  So, as you can imagine it is quite easy to create orphaned web part instances in the content database that will not be discovered until the upgrade.

Content database can help

Fortunatelly, you can run SQL query against content database using WebPartTypeId as a parameter to retrieve list of web part references. 

SELECT wp.tp_WebPartTypeId 
    , w.FullUrl
    , doc.DirName WebPartPageDirectory
    , doc.LeafName WebPartPageName
    , doc.SiteId
    , doc.WebId
    , wp.tp_ZoneId
 FROM AllDocs doc
INNER JOIN WebParts wp
   ON doc.Id = wp.tp_PageUrlID 
INNER JOIN [Sites] s
   ON doc.SiteId = s.Id 
   ON doc.WebId = w.Id
WHERE wp.tp_WebPartTypeId IN ('WebPartId here')

Sometimes, the page with MissingWebPart may not appear in the User Interface and may not be accessible even through SharePoint API. This maybe caused by page being checked-out by the user and left in draft version forever. You can verify it and remove it (consult it with the end-user) by going to the Page library settings page.


MissingWebPart error can be resolved in several way
  • Add missing web part assembly to the GAC
  • Remove ErrorWebPart web part instance from the page (either through UI using WebPart maintenance page by adding ?contents=1 to the page URL)
  • Remove ErrorWebPart instsnce through SharePoint API
  • Remove page hosting MissingWebPart instance
It may be that you will not be able to remove MissingWebPart instance. I've run into situations where Page, or whole Libraries where orphaned (not accessible through SharePoint API or UI) and nothing could be done to resolve it.

To avoid this kind of errors always migrate existing webpart instances when changing assembly versions, either through assembly binding (redirect assembly to the new version) or through webpart export and import process.

Hope this helps.