Sunday, September 28, 2014

Programmatically attach and detach event receivers to specific Sharepoint list safely

In Sharepoint development we often need to attach/detach event receivers to specific list (by specific list I mean single list on the web site where feature is activated, not to all lists created with particular list template). So I decided to make this post for future references also for myself. As title says it will show how to attach event receivers to particular event type (e.g. ItemAdded or ItemUpdated) safely, i.e. if feature will be activated several time with force flag, the same event receiver won’t be attached again and as result it won’t be triggered several times which may cause problems on your environment.

We will need feature with feature receiver: attaching of list event receiver will be made in FeatureActivated() method and detaching in FeatureDeactivating():

   1: public class TestFeatureReceiver : SPFeatureReceiver
   2: {
   3:     public override void FeatureActivated(SPFeatureReceiverProperties properties)
   4:     {
   5:         var web = properties.Feature.Parent as SPWeb;
   6:         this.addEventReceiverToSitesList(web);
   7:     }
   8:  
   9:     private void addEventReceiverToSitesList(SPWeb web)
  10:     {
  11:         var list = web.Lists.Cast<SPList>().FirstOrDefault(l => l.Title == "test")
  12:         if (list == null)
  13:         {
  14:             return;
  15:         }
  16:  
  17:         bool listWasChanged = false;
  18:         this.ensureEventReceiverAdded(web, list.EventReceivers,
  19:             SPEventReceiverType.ItemAdded, ref listWasChanged);
  20:         this.ensureEventReceiverAdded(web, list.EventReceivers,
  21:             SPEventReceiverType.ItemUpdated, ref listWasChanged);
  22:         if (listWasChanged)
  23:         {
  24:             list.Update();
  25:         }
  26:     }
  27:  
  28:     private void ensureEventReceiverAdded(SPWeb web,
  29:         SPEventReceiverDefinitionCollection receivers, SPEventReceiverType type,
  30:         ref bool wasChanged)
  31:     {
  32:         if (receivers == null)
  33:         {
  34:             return;
  35:         }
  36:  
  37:         if (receivers.Cast<SPEventReceiverDefinition>().Any(e => e.Type == type &&
  38:             e.Assembly == typeof(TestEventReceiver).Assembly.FullName &&
  39:             e.Class == typeof(TestEventReceiver).FullName))
  40:         {
  41:             // event receiver is already attached for specified event type
  42:         }
  43:         else
  44:         {
  45:             receivers.Add(type, typeof(TestEventReceiver).Assembly.FullName,
  46:                 typeof(TestEventReceiver).FullName);
  47:             wasChanged = true;
  48:         }
  49:     }
  50:  
  51:     public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
  52:     {
  53:             var web = properties.Feature.Parent as SPWeb;
  54:             this.removeEventReceiverFromSitesList(web);
  55:     }
  56:  
  57:     private void removeEventReceiverFromSitesList(SPWeb web)
  58:     {
  59:             var list = web.Lists.Cast<SPList>().FirstOrDefault();
  60:             if (list == null)
  61:             {
  62:                 return;
  63:             }
  64:  
  65:             bool listWasChanged = false;
  66:             this.ensureEventReceiverRemoved(list.EventReceivers,
  67:                 SPEventReceiverType.ItemAdded, ref listWasChanged);
  68:             this.ensureEventReceiverRemoved(list.EventReceivers,
  69:                 SPEventReceiverType.ItemUpdated, ref listWasChanged);
  70:             if (listWasChanged)
  71:             {
  72:                 list.Update();
  73:             }
  74:     }
  75:  
  76:     private void ensureEventReceiverRemoved(
  77:         SPEventReceiverDefinitionCollection receivers, SPEventReceiverType type,
  78:         ref bool wasChanged)
  79:     {
  80:         if (receivers == null)
  81:         {
  82:             return;
  83:         }
  84:  
  85:         var itemAddedReceiver = receivers.Cast<SPEventReceiverDefinition>().
  86:             FirstOrDefault(e => e.Type == type &&
  87:                 e.Assembly == typeof(TestEventReceiver).Assembly.FullName &&
  88:                 e.Class == typeof(TestEventReceiver).FullName);
  89:         if (itemAddedReceiver != null)
  90:         {
  91:             itemAddedReceiver.Delete();
  92:             wasChanged = true;
  93:         }
  94:     }
  95: }

Like shown in example above before to add or remove event receiver for specified event type it checks first that event receiver is attached to the list or not. I.e. it will be safe to activate this feature multiple times with force parameter on the same web site and then deactivate it. Also using this technique it will be possible to safely attach event receivers to another event types after feature was already activated: it won’t attach the same receiver to existing events and will only attach it for new ones.

1 comment: