Handling an orphaned SPPersistedObject when working with e.g. SPJobDefinition or SPDiagnosticsServiceBase
I've been working with improving our Puzzlepart Framework components for simplifying development of SharePoint Timer Jobs lately, in relation to our upcoming release of "Did - The calendar is the timesheet" business app for SharePoint, and (as usual when digging deep into SharePoint) I ran into some problems with our beloved platform that needed resolution:-)
If you're about to work with any type of SPPersistedObject I recommend you to first read through Charlie Hollands "The skinny on SPPersistedObject and the Hierarchical Object Store in SharePoint 2010" to make sure you've got the basics right.
The problem
Creating SPPersistedObjects usually
happens when you call Update() on something like your
SPJobDefinition subclass. In the case of a custom logger,
the creation happens indirectly through the call to
SPDiagnosticsServiceBase.GetLocal<T>() (which fails if
the object is already there). This is all well and good,
until you decide to rename or move your class, and redeploy
your solution. The only way do delete an SPPersistedObject
is to call Delete() on the object and the only way to get a
SPPersistedObject is to call GetChild<T>(name). Thus
if your typeof(T) is not loadable in the current context you
cannot get the object and thus cannot delete it.
Usually this will only create orphans in your config db that will spam your ULS logs with typeload errors, but sometimes it also blocks your new or updated type from working properly. In our case we were experimenting with ILMerge and (temporarily) had the same class defined in multiple assemblies in our environment which totally confused SharePoint. It seems as the object-exists logic for SPPersistedObjects uses only the typename, while the creation logic pulls the AssemblyQualifiedName from the configdb, making the orphaned object registration a blocker for the new one.
The supported (totally undocumented solution)
After blogging this my colleague
KjellSJ
brought to my attention the undocumented
deleteconfigurationobject stsadm command. SharePoint
blogging legend Bil Simser
wrote about this a loong time ago, and he'll help you avoid my SQL nastyness below:-)
The (totally unsupported) solution
I could
obviously revert my svn trunk and try to rebuild and deploy
my old component-setup in order to allow loading of
the orphan, but that seemed just too painful in a
dev-environment. Que SQL:
USE MYSHAREPOINTCONFIGDB
SELECT [Id],
[ClassId],
[ParentId],[Name],[Status],[Version],[Properties]
FROM [Objects]
WHERE ClassId in
(SELECT [Id]
FROM [Classes]
WHERE FullName like 'Pzl.Framework%')
Obviously replace your typename in the last statement. This
will allow you to see the object registrations and I'll let
you figure out the delete statement for those yourself:-)
When the invalid object registrations are gone, you can take
care of the entries in the Classes table. Totally
unsupported but helpful for your dev environment when you
don't want to recreate your WebApplication or farm.