May 24, 2012

Configuring a List Event Receiver in a Site-Scoped Feature

I was working on a fairly standard site-scoped SharePoint feature yesterday which included a content type, list definition, list instance, and an event receiver to handle the ItemAdded event on all instances of the list definition (as well as a bunch of other stuff not really relevant to this post). The feature needed to be scoped at the Site level. Not a big deal, right?

Well, it turns out there is apparently a bug in how event receivers are bound to list templates or instances when deployed inside of a site-scoped feature. Say you add a new event receiver using Visual Studio. Here's the elements.xml file that will be generated:

  
      
        EventReceiver1ItemAdded
        ItemAdded
        $SharePoint.Project.AssemblyFullName$
        MyProject.EventReceiver1.EventReceiver1
        10000
      
  

Cool. If you want to bind the receiver to a specific list instance rather than a list definition/template, you'd just replace the listtemplateid="10001" with listurl="Lists/MyList". Regardless, this should compile, deploy, and work perfectly without any trouble.

And it does... if your feature is scoped at the Web level. But for some unknown reason, if your feature is scoped at the Site level, the receiver will bind to all list instances in the site collection. Eek! There aren't too many scenarios in which that's a good idea. So how can we fix this?

MSDN is always the first place I go for information when I run into any trouble - maybe I'm just missing an additional attribute or something? And sure enough, there is a "scope" attribute on the <receivers> element that I hadn't known about before. I got excited until I read the description and learned that it basically does nothing:

Optional enumeration. The scope of the event receiver. This can be Site or Web. When the Scope attribute is set to Site, event receivers that are defined inside of a Feature that is scoped to the site collection are registered at the site collection level. When the Scope attribute is set to Web, event receivers that are defined inside of a Feature that is scoped to a Web are registered on the Web.

Like, duh? I did try setting this to each value for good measure but neither solved the problem. Bummer dude. So, I reluctantly decided to bind the receiver to my list instance via code rather than XML. I had already created a feature event receiver for some other functionality, so I simply added the following code to the FeatureActivated event:
SPWeb web = ((SPSite)properties.Feature.Parent).RootWeb;
web.GetList(web.ServerRelativeUrl + "/Lists/MyList").EventReceivers.Add(SPEventReceiverType.ItemAdded, 
            properties.Definition.ReceiverAssembly,
            "MyProject.EventReceiver1.EventReceiver1");
This worked without any trouble, which was a relief. But still, as someone who always prefers to configure everything via XML rather than through code, it was disappointing to be forced into this. Not to mention frustrating at first when I knew I had everything wired up correctly and yet my list events were firing in all the wrong places!

I'd be surprised if developers didn't commonly run into this, but it took more Googling than normal to figure out what was going on, so I wanted to share my discovery with the rest of you. I hope this helps!

No comments:

Post a Comment