Partager via


A behind-the-scenes look at MissingWebPart errors from the "Missing server side dependencies" health analyzer rule

Recently I was helping a customer with a review of their SharePoint Server 2010 environment when I noticed a ton of "Missing server side dependencies". Most notably were missing Web parts and even more notable was that they were OOTB Web parts. The entries were listed like this:

[MissingWebPart] WebPart class [1ce3ddc9-1d7f-3ecb-b9d3-ee015154456b] is referenced [3] times in the database [WSS_Content_4992], but is not installed on the current farm. Please install any feature/solution which contains this web part. One or more web parts are referenced in the database [WSS_Content_4992], but are not installed on the current farm. Please install any feature or solution which contains these web parts.

[MissingWebPart] WebPart class [7494019e-cc3c-dc3d-88ee-f9782d55ba37] (class [Microsoft.SharePoint.Publishing.WebControls.TableOfContentsWebPart] from assembly [Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c]) is referenced [1] times in the database [WSS_Content_4992], but is not installed on the current farm. Please install any feature/solution which contains this web part. One or more web parts are referenced in the database [WSS_Content_4992], but are not installed on the current farm. Please install any feature or solution which contains these web parts.

There are tons of information on the Internet regarding missing server side dependencies for the Central Administration content database with fixes like enabling SharePoint Foundation Search, click on those two pages for Search administration and then rerun the rule. I could not find any legitimate solution for this issue regarding a normal Team site content database.

My first thought was that somehow the Publishing feature would be the culprit, but after deactivating and reactivating the Publishing Feature, rerunning the rule, the errors persisted. This then led me down the path of looking at the WFE servers in the farm for the existence of these features/solutions. I was able to locate the .webpart files and features containing a lot of the Web parts in the SharePoint Root directory.

I started digging into the Health Analyzer rule for "Missing server side dependencies" and information learned from that exploration is shared here to provide a better understanding of how missing server side dependencies appear in the health analyzer.

The first thing I wanted to know is what controls the missing server side dependencies rule. This information is stored in the Central Administration site under the List HeathRules. From here, you can edit a few properties about the rule. For example, its Title, Scope (whether to run against ANY or ALL servers in the farm), Schedule, whether its enabled, Repair Automatically and Version. That's great, but where is the meat of the rule? Turns out what I needed was stored in Hidden fields of the list. To get this information, I created a Console Application using Visual Studio to iterate the HealthRules list, pulling out the Fields Collection of the "Missing server side dependencies" rule.

 

One property in particular is the one we're after:

 <Field ID=\"{7dd0a092-8704-4ed2-8253-ac309150ac59}\" Name=\"HealthRuleType\" DisplayName=\"Type\" Type=\"Text\" Description=\"The rule type specifies the fully-qualified assembly name of the rule class.\" Group=\"_Hidden\" SourceID=\"https://schemas.microsoft.com/sharepoint/v3/fields\" StaticName=\"HealthRuleType\" AllowDeletion=\"FALSE\" Hidden=\"TRUE\" Required=\"TRUE\" ColName=\"nvarchar3\"/>

The field contains the value of:

 Microsoft.SharePoint.Administration.Health.ContentDatabaseCorruption, Microsoft.SharePoint.Health, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c

 

Now, we have something to work with. I pull the Microsoft.SharePoint.Health.dll out of the GAC and use the open-source de-compiler JetBrains dotPeek to look at the source code. This is a synopsis of my code review.

The ContentDatabaseCorruption class contains a Check() method that returns a SPHealthCheckStatus object. This method create a SPWebServiceCollection object and performs a foreach loop, basically getting at all of the SPWebApplication objects in the collection. It then creates a SPContentDatabaseCollection, iterates over the collection and then calls this bit of code:

  

 SPContentDatabaseIntegrity databaseIntegrity = new SPContentDatabaseIntegrity(db.WebApplication, db, true, SPManager.Instance.Log);
 
databaseIntegrity.Check(true);
 
if (databaseIntegrity.TestResults.Count > 0)
 
this.m_sbInfo.AppendLine(databaseIntegrity.GetResult());

 

 As you can see, we then have to review the SPContentDatabaseIntegrity class to see what's going on in there. In the SPContentDatabaseIntegrity class, it does a lot of stuff, but what's interesting to us is the Check() method which is overloaded taking a Boolean parameter of whether it's coming from the health analyzer.

 

  this.CheckMissingWebTemplates();
 this.CheckMissingFeatures();
 this.CheckOrphanedSites();
 this.CheckSetupFiles();
 this.CheckWebParts();
 this.CheckEventReceiverAssemblies();
 if (calledFromHealthRule)
 return;
 this.AssertValidMaxRowOrdinalInAudTable();
 this.CheckListSchemaLimits();

As you can see, it performs all of these checks for each content database for each Web application. We'll focus on the this.CheckWebParts(); method for this blog post.

One of the first steps is to declare the SQL command used for the rest of the routine. I'm sure this will look familiar to those that have had to troubleshoot these issues in the past.

 

 SELECT tp_WebPartTypeId, COUNT(1), tp_Assembly, tp_Class FROM AllWebParts
 
 (NOLOCK) WHERE tp_WebPartTypeId IS NOT NULL AND tp_SolutionId IS NULL
 
 GROUP BY tp_WebPartTypeId, tp_Assembly, tp_Class

This query actually varies based on the SchemaVersion, but is not that important here. 

 The next step is important and I had never thought this played into the rule, but it is a crucial piece to recognize. Using the Microsoft.SharePoint assembly it creates a new Microsoft.SharePoint.ApplicationRuntime.SafeControls object passing in the Web application and the Default zone of the Web application. At this point, I'll give you the solution to the problem I was having.

 When the SharePoint Timer job starts and based on the Scope of the rule (Any Server), it will sometime vary which Server in the farm the rule is applied against. In my case, the Application server was always the server to be picked. Back up in the ContentDatabaseCorruption.Check() method, it creates the SPWebServiceCollection by passing in SPFarm.Local:

 

 SPWebServiceCollection serviceCollection = new SPWebServiceCollection(SPFarm.Local);

 

This will return the current server farm object. Because this server wasn't running the Web application role, the IIS Website doesn't exist. Moreover, the web.config file it expects to find to create the SafeControls object didn't exist either.

To avoid this issue, either start the Web application role on each server in the farm or replicate the virtual directory structure for each Web application to every server not hosting the Web application role. I would choose the former so it doesn't become a maintenance task each time you create a new Web application. 

 

Bottom line: This health analyzer rule uses the results of the T-SQL query above and looks for a SafeControl entry that matches the GUID (tp_WebPartTypeId), assembly name (tp_Assembly) and type name (tp_Class).

If no match is found, it determines if the Assembly or Type name is not null. If this evaluates to true, it appends this to the text that you see in the list. Otherwise, it just omits this information from the entry. From here, it adds the entry to a Generic List and moves on the next record. In a later post, I will cover the other missing server side dependencies tests.