How to store each SharePoint Site Collection in its own Database – Part 3

As requested in Part 2, here is the feature code to add eventreceivers to all existing and new site collections. In my experience, I like to create a Web scoped feature that does the work for one web, and then write another feature scoped at the site collection or web application level that activates my web scoped feature for existing and new webs.

So first off, here is the code that does the work for one web:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebPartPages;
using System.Web.UI.WebControls.WebParts;
using System.Web;
using Microsoft.SharePoint.Administration;

namespace Features.SiteCollectionDeletedEventReceiverWeb
{
    class FeatureReceiver : SPFeatureReceiver
    {
        public override void FeatureInstalled(SPFeatureReceiverProperties properties) { }
        public override void FeatureUninstalling(SPFeatureReceiverProperties properties) { }
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            using (SPWeb web = (SPWeb)properties.Feature.Parent)
            {
                if (web == null) throw new ApplicationException("Web could not be found");

                web.EventReceivers.Add(SPEventReceiverType.SiteDeleted,
                    "EventHandlers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XXXXXXXXXXXXXXXX",
                    "EventHandlers.DeleteDatabaseOnSiteDeleted");
                web.EventReceivers.Add(SPEventReceiverType.SiteDeleting,
                    "EventHandlers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XXXXXXXXXXXXXXXX",
                    "EventHandlers.DeleteDatabaseOnSiteDeleted");
                web.Update();
            }
        }
        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            using (SPWeb web = (SPWeb)properties.Feature.Parent)
            {
                if (web == null) throw new ApplicationException("Web could not be found");

    List<SPEventReceiverDefinition> eventReceiversToDelete = new List<SPEventReceiverDefinition>();

    foreach (SPEventReceiverDefinition eventReceiver in web.EventReceivers)
    {
     if (eventReceiver.Class == "EventHandlers.DeleteDatabaseOnSiteDeleted")
     {
      eventReceiversToDelete.Add(eventReceiver);
     }
    }

    foreach (SPEventReceiverDefinition eventReceiver in eventReceiversToDelete)
    {
     eventReceiver.Delete();
    }

    web.Update();
            }
        }
    }
}

And here is the code that activates the previous feature on all existing webs for the whole farm:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace Features.SiteCollectionDeletedEventReceiverWebApplication
{
    class FeatureReceiver : SPFeatureReceiver
    {
        public Guid FeatureId()
        {
            return new Guid("{0C522324-C774-4424-B73E-1BCDEC147D89}");
        }

  public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            SPWebApplication webApplication = properties.Feature.Parent as SPWebApplication;
            if (webApplication == null) throw new ApplicationException("WebApplication could not be found");

            foreach (SPSite site in webApplication.Sites)
            {
                foreach (SPWeb web in site.AllWebs)
                {
                    if (false == IsFeatureActive(FeatureId(), web.Features) &&
                        ShouldActivateFeature(web))
                    {
                        web.Features.Add(FeatureId(), true);
                    }
                    web.Dispose();
                }
                site.Dispose();
            }
        }

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            SPWebApplication webApplication = properties.Feature.Parent as SPWebApplication;
            if (webApplication == null) throw new ApplicationException("WebApplication could not be found");

            foreach (SPSite site in webApplication.Sites)
            {
                foreach (SPWeb web in site.AllWebs)
                {
                    if (IsFeatureActive(FeatureId(), web.Features))
                    {
                        web.Features.Remove(FeatureId(), true);
                    }
                    web.Dispose();
                }
                site.Dispose();
            }
        }

  public override void FeatureInstalled(SPFeatureReceiverProperties properties)
        {

        }

        public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
        {

        }

        private bool ShouldActivateFeature(SPWeb web)
        {
   //Implement any custom logic whether to activate the feature on the web
            return true;
        }

  private bool IsFeatureActive(Guid featureId, SPFeatureCollection features)
        {
            try
            {
                SPFeature feature = features[featureId];
                if (feature != null) return true;
            }
            catch { }
            return false;
        }
    }
}

Also, this is feature stapling code in the feature_elements.xml file that activates the feature on any new webs:

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
 <FeatureSiteTemplateAssociation Id="0C522324-C774-4424-B73E-1BCDEC147D89" TemplateName="GLOBAL" />
 <FeatureSiteTemplateAssociation Id="0C522324-C774-4424-B73E-1BCDEC147D89" TemplateName="STS#1" />
</Elements>

The global means it will be activated no matter what template is selected. I found that in some cases, a template (such as the blank template, STS#1) will not allow global based feature stapling. That’s why I added the extra line for STS#1, which is the blank template.

The full source can be downloaded here.

If you are going to copy my source, please remember a few things:

  1. Make sure you sign your assemblies
  2. EventReceivers are REQUIRED to be in the GAC
  3. Make sure you replace the ReceiverAssembly and ReceiverClass with your assembly and class names including the publickeytoken in the Feature.xml files
  4. Do the same thing for the EventReceivers in the SiteCollectionDeletedEventReceiverWeb FeatureReceiver

Leave a Reply

Your email address will not be published. Required fields are marked *

*

*