With the help of MS support, we finally got to the bottom of this issue. It turned out to be related to the content database upgrade (from MOSS 2007 to 2010) that had taken place months before. Ultimately, there was no object model fix for the problem, and the SharePoint product group ended up giving us permission (and code) to modify the content database directly to resolve the problem.
DISCLAIMER: Modifying the content database directly typically puts your SharePoint server into an unsupported state, and is NOT recommended. In this case, however, we had permission from Microsoft to do so as there was no other way to resolve our problem.
I'll try to make a long story short. Basically, if you have event receivers attached to content types at the time of a content database upgrade, you may be affected by this issue. In order to understand the problem, and ultimate solution, you need to know a couple of pieces of information:
- Event receivers for content types are stored in 2 tables in the content database: (1) dbo.ContentTypes (2) dbo.EventReceivers
- The dbo.EventReceivers table contains a column for each property on the SPEventReceiverDefinition class, making it easy to see what receivers are present.
- The dbo.ContentTypes table contains a column called "Definition" that contains the CAML definition of the content type, including any event receivers that are added (programmatically or declaratively) to the content type.
- The CAML definition contains XML, but the event receivers portion is base-64 encoded, making it very difficult to see what receivers are present (SharePoint manager comes in handy here, if you locate the content type under the desired web and click on the schema.xml tab).
Using the SharePoint object model to delete an event receiver on a content type would typically look something like this:
SPContentType ct = web.ContentTypes["My Content Type"];
ct.EventReceivers[0].Delete();
ct.Update(true);
Executing this code should cause the event receivers to disappear from both the dbo.ContentTypes and dbo.EventReceivers tables. This is precisely what we had been unable to do. No matter how many times, or different ways, we tried to run this code, the event receivers always remained.
Using Reflector (or a similar tool), you can look at the implementation of SPEventReceiverDefinition::Delete(), which is the second line in our code snippet above. You can then follow this method through a series of calls to the SPEventReceiverDefinitionCollection::DeleteContentTypeEventReceiver(SPEventReceiverDefinition) method.
This method loops over all of the event receivers in the collection (from the dbo.ContentTypes table) and compares them property-by-property to the event receiver passed in (which is from the dbo.EventReceivers table). For each receiver in the collection, if it does not match the receiver passed in, it is written back to the dbo.ContentTypes table (base-64 encoded in the "Definition" column). Otherwise, it is skipped (and therefore not written back to the database). I assume it does this to avoid conflicts with updates that might have been triggered from other threads or servers in the farm, which may have rendered the receiver being deleted out of date.
The key in all of this is that if an event receiver in one table doesn't exactly match a receiver in the other table, then there is no way to remove it. In our case, we discovered that the value of the "Synchronization" field had been set to "Default" for every receiver in the dbo.ContentTypes table. However, in the dbo.EventReceivers table, the value was set to either "Synchronous" or "Asynchronous", depending on the type of event. Since this property (SPEventReceiverDefinition.Synchronization) is new for SharePoint 2010, we are assuming that the content database migration/upgrade incorrectly set those values.
The fix was to:
1. Read the content type definition CAML directly out of the content database
2. Decode the base-64 encoded receivers
3. Modify the "Synchronization" property to be either "Synchronous" or "Asynchronous"
4. Base-64 encode the receivers
5. Update the content type definition CAML directly in the content database
After completing this process, we were finally able to remove the content type event receivers using the normal object model methods. Note that we could temporarily work around the problem by creating a new site collection, as the content types are scoped to the site collection level. I would expect to see a hotfix come out for this eventually, but for now I hope this information is helpful.