17

I have a very odd situation in my SharePoint staging environment. We recently stood up a new SharePoint 2010 server (single WFE + a DB server), and attached a backed-up content database from our existing environment. We created a new web application, and pointed it at the attached content database. All of our site collections, sites, lists, etc. appeared, and things appeared good.

We had deployed some custom content types to our existing environment prior to moving the database, and we wanted to upgrade those content types. Specifically, we attach event receivers to the content types (using code, not XML) and we needed to update the assembly version that those event receivers point to. So we ran our usual code (part of a feature receiver) to remove the event receivers, but to our surprise, the receivers remained.

In an attempt to remedy the situation, we wrote a console application that iterates over all content types (SPWeb.ContentTypes) in the root site of each site collection and deletes them, and then calls SPContentType.Update(true) on each content type. There are no errors returned from the call to Update, but again to our even greater surprise, SharePoint still reports the event receivers are attached.

In a desparate last ditch effort, we even went into the content database (after taking a snapshot -- and remember, this is staging, not production!) and manually DELETED the offending receivers from the EventReceivers table. We figured that should have at least some kind of effect. Alas, SharePoint still reports the receivers as being present.

We perform these types of upgrades on content type event receivers all the time, but have never run into this issue on any other SharePoint farm. Does it sound like an environmental problem? Is it something that could have been caused by moving the content database? Any help would be appreciated, because we are completely stumped at this point.

8 Answers 8

9

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.

1
  • Could you shed more light on how to perform the fix? How does one 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?
    – TempaC
    Nov 7, 2012 at 13:58
4

Have you tried the SP Event Handler Manager on codeplex?
http://speventhandlermanage.codeplex.com/

2
  • Thanks for the suggestion, Mike. I don't have access to the server at this moment, so I can't test it, although I've used this tool before. It works in the same way that our own tools work, so I'm not very hopeful. Any more thoughts?
    – JasonV
    Jan 22, 2012 at 21:58
  • I tried code.google.com/p/sharepoint-eventhandlers-manager/wiki/… but in my (broken) setup the events are not showing.
    – TempaC
    Nov 7, 2012 at 12:52
2

Thank you for your comments on this issue. I have experienced a similar problem with our content types/event receivers after upgrading a 2007 content db to 2010.

Calling the Delete() method on the associated event receviers seemed to work on the surface, but upon subsequent inspection of the content type the event receiver is still present.

I have managed to find a supported mechanism to remove the event receivers from the content model using the SharePoint object model.

Inspection of the content type object shows a XmlDocuments object which directly correlates to the XmlDocument elements in the content type XML definition described by Jason (visible using SharePoint manager). This object has a Delete method which you can use to remove XmlDocument entries from the collection. This works perfectly for my scenario as I wish to remove all event receivers. If you wanted to only remove individual event receivers you could re-add them after removing the entry.

using (var sc = new SPSite(url))
{
    using (var web = sc.OpenWeb())
    {
        var ct = web.ContentTypes[ctype];
        ct.XmlDocuments.Delete("http://schemas.microsoft.com/sharepoint/events");
        ct.Update(false); // 'true' if you want to propagate to derived ctypes
    }
}

Hopefully this helps someone as much as the other posts helped me!

Cheers

J

1
  • Your solution worked perfectly for me. And the main thing is that I did not touch the content database directly. This last option was unworkable because it was the production environment. Document analysis in surpoint.blogspot.com/2015/09/…. James thank you very much! Sep 23, 2015 at 11:36
1

Ok, let's start from the basics...

Before you deactivate the Content Types feature you need to make sure that no items or lists are using the feature. Also make sure you that are not using the contenttypebinding element in your feature as that will also prevent you from deleting the content type.

Secondly, make sure to use SPContentTypeUsage class to track where a content type is used as the basis for another content type. The static/shared GetUsages method returns a list of SPContentTypeUsage objects that contain information about the content type usage, such as the ID, URL and whether or not the URL is the URL to a list.

Find all the usage and remove the Content types properly again, because event receivers are added before adding a content type to a list, site etc

1

We've run into issues with event receivers too. You might have an orphaned event receiver, which you can delete via a PowerShell command: http://blogs.msdn.com/b/sharepoint_bar__grill/archive/2011/04/09/delete-orphaned-event-handler-through-powershell.aspx

As per that article (by SharePoint Mike), if you know the assembly, use these two commands, replacing the quoted item with your assembly info:

$var = Get-SPSite -limit All | Get-SPWeb -limit all | % {$_.EventReceivers} | where {$_.Assembly -eq "ReplicatorEventHandler, Version 1.0.0.0, Culture=neutral, PublicTokenKey=ccd0742fa6b8e563"} 
$var | % {$_.Delete()}

If it's not known, you can get all event receivers (again from that article):

$var = Get-SPSite -limit All | Get-SPWeb -limit all | % {$_.EventReceivers} | where {$_.Assembly -like "*"} | Select Assembly
2
  • We are working this issue with MS support right now, so if we get it resolved I'll report back here. We've rolled back to a previous snapshot, so if we get it in the bad state again, I'll give this PowerShell a try. Thanks!
    – JasonV
    Jan 23, 2012 at 22:38
  • 1
    I think the powershell mentioned do not show the event receiver for content type. It only show the ER for SPSite and SPWeb
    – Mark L
    Feb 21, 2017 at 4:00
1

I've got a similar situation right now. Post-migration, there are three event receivers that belong to a non-existent site, but a correct web (SiteID bad, WebID good).

Those can't be deleted via object model -- i.e. no script or console app will remove them -- because there's no SPSite through which they're visible. There's a FeatureAdmin script that looked like it might work, but it fails for this exact reason.

One possibility I tried was to "leave them behind" by using the Move-SPSite cmdlet to move the entire site collection into a fresh content database. Since Move-SPSite uses the same API as we do, it didn't see the orphaned event receivers while moving the site.

However, something in what Move-SPSite did caused queries involving LookupMulti columns to slow down by a factor of 100 (i.e. what used to take a few milliseconds began to take 20-30 seconds).

The short: no good solution as yet.

1
  • Do you have a status update on your solution or workaround? How did you solve your problem?
    – TempaC
    Nov 7, 2012 at 14:24
1

We had the same issue after our upgrade from 2007 to 2010. We were able to fix this issue using another method which is not as extreme as updating Content Databases after getting MS support involved. Basically, instead of deleting the eventreceivers from the content type, we can redirect it to the newer version through the app.config file in the case of running a windows application. SharePoint applications do this automatically by using the following <assemblyBinding> tag in the web.config which looked like the following in our case:

<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly xmlns="urn:schemas-microsoft-com:asm.v1">
        <assemblyIdentity name="Microsoft.Office.Policy" publicKeyToken="71e9bce111e9429c" culture="neutral" />
        <bindingRedirect oldVersion="12.0.0.0" newVersion="14.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>

So, we put up <assemblyBinding> tag under the <runtime> tag in the app.config and everything started working as expected.

0

Thanks for sharing this, James. I managed to get rid of my orphaned Content type Event Receivers by improving your example. This keeps non-event-Receiver related XML code in place.

        SPSite site = new SPSite("http://sr-dev-sp2");
        using (var web = site.OpenWeb())
        {
            foreach (SPContentType ct in web.ContentTypes)
            {
                SPEventReceiverDefinitionCollection spEDC = ct.EventReceivers;
                if (spEDC.Count > 0)
                {
                    SPXmlDocumentCollection xmlColl = ct.XmlDocuments;
                    for (int i = xmlColl.Count - 1; i >= 0; i--)
                    {
                        string doc = xmlColl[i];
                        bool updateXML = false;
                        XmlDocument xmlDoc = new XmlDocument();
                        xmlDoc.LoadXml(doc);
                        if (String.Compare(xmlDoc.DocumentElement.Name, "spe:Receivers", false) == 0)
                        {
                            XmlNode root = xmlDoc.FirstChild;
                            XmlNodeList receivers = root.ChildNodes;
                            for (int j = receivers.Count -1; j>=0; j--)
                            {
                                XmlNode rec = receivers[j];
                                XmlNode assembly = rec.SelectSingleNode("Assembly");
                                string assemblyString = assembly.FirstChild.Value;
                                try
                                {
                                    System.Reflection.Assembly eventReceiverAssembly = System.Reflection.Assembly.Load(assemblyString);
                                }
                                catch
                                {
                                    root.RemoveChild(rec);
                                    updateXML = true;
                                }
                            }
                        }
                        if (updateXML)
                        {
                            ct.XmlDocuments.Delete(xmlDoc.FirstChild.NamespaceURI);
                            ct.XmlDocuments.Add(xmlDoc);
                            ct.Update(true); // true to propagate to derived ctypes
                        }
                    }
                }
            }
        }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.