Ivan @ imason

Advice and opinions on SharePoint and ASP.NET development matters.

Organize your Feature Receivers

  • Comments 1

Assuming that you deal a lot with installation of various SharePoint components and solutions, and have written custom feature receivers a number of times, you have probably seen the below code fragment each time you wrote a site collection-scoped feature.

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    SPSite site = properties.Feature.Parent as SPSite;
    // more code here...

I think that if you are working on several feature receivers, then you don’t want to repeat this and likely a lot of other lines of code within each feature receiver. This post is about a simple way to avoid code repetition in your feature receivers and organize their logic better. All you need to do is define a set of base feature receiver classes for various feature scopes, i.e. web, site collection, web application or a farm, and have your actual feature receivers be the children of these types. Here is an example of site collection base feature receiver:

/// <summary>
/// Base feature receiver for site-scoped features. Allows optional 
/// implementation of installation/unisnstallation event handler 
/// methods by its children.
/// </summary>
public abstract class SiteFeatureReceiver : SPFeatureReceiver
{
    #region SPFeatureReceiver Implementation

    public override void FeatureActivated(SPFeatureReceiverProperties properties)
    {
        SPSite site = properties.Feature.Parent as SPSite;

        if (null == site)
        {
            //
            // The feature is not site-scoped. Do nothing.
            //

            Console.WriteLine(
                "The feature '{0}' is not defined as site-scoped. " +
                "Skipping activation handler.",
                properties.Feature.Definition.Name);
            return;
        }

        OnFeatureActivated(properties, site);
    }

    public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
    {
        SPSite site = properties.Feature.Parent as SPSite;

        if (null == site)
        {
            //
            // The feature is not site-scoped. Do nothing.
            //

            Console.WriteLine(
                "The feature '{0}' is not defined as site-scoped. " +
                "Skipping de-activation handler.",
                properties.Feature.Definition.Name);
            return;
        }

        OnFeatureDeactivating(properties, site);
    }

    public override void FeatureInstalled(SPFeatureReceiverProperties properties)
    {
        // Do nothing.
    }

    public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
    {
        // Do nothing.
    }

    #endregion

    #region Abstract Members

    /// <summary>
    /// When overriden in child classes performs 
    /// actions in response to feature activation event.
    /// </summary>
    /// <param name="properties">Activation context properties 
    /// passed by SharePoint infrastructure.</param>
    /// <param name="site">Site collection the feature is related to. 
    /// It is assumed that the feature is site-scoped.</param>
    protected abstract void OnFeatureActivated(
        SPFeatureReceiverProperties properties,
        SPSite site);

    /// <summary>
    /// When overriden in child classes performs 
    /// actions in response to feature deactivation event.
    /// </summary>
    /// <param name="properties">Activation context properties 
    /// passed by SharePoint infrastructure.</param>
    /// <param name="site">Site collection the feature is related to. 
    /// It is assumed that the feature is site-scoped.</param>
    protected abstract void OnFeatureDeactivating(
        SPFeatureReceiverProperties properties,
        SPSite site);

    #endregion
}

As you can see, you can define types inheriting from your SiteFeatureReceiver type, which would need to override OnFeatureActivated() and OnFeatureDeactivating() methods with type-safe SPSite parameter provided. Also, I didn’t write methods to handle installed/uninstalling events, as their use is quite rare, but they can be easily added if so required. So you may ask “all this code is there just to save you from casting a feature parent from object to SPSite or SPWeb?” Certainly not, as this approach gets you much more value strategically. One example where base feature receiver is useful – it is the perfect place for adding exception handling logic saving you from repeating same lines all over again in each feature receiver. Think about it – outputting messages to the console could be made uniform if you add message formatting facility in your base feature receiver, or you can add logic for attaching a debugger to your feature receivers to the base class. There is plenty of flexibility, it is the same old pattern making the inheritance useful.

 

One note on exceptions. There are different business requirements when it gets down to handling and logging exceptions. Sometimes you must log them in a specific format so that a server monitor software could collect them, but if you do have the freedom of choice of how to log exceptions when writing your feature receiver, I would recommend not doing that at all! Instead of focusing on logging exceptions, focus on throwing them whenever any of assumptions you have made as an author of a feature receiver is violated. When you throw an exception, make it specific, meaning that it should not just complain about a problem, but provide information about what precisely is missing – a specific file path, a URL, etc. This way your exception will provide an immediate clue for troubleshooting the problem. Why not log exceptions from within feature receivers? Well, when you activate features from command prompt, firstly if talking about Windows event log, I tend to treat it as a destination for run-time messages, not as much for installation messages; secondly if talking of a text log file, then it is easy to pipe your output to a text file from command prompt or installation batch script or even make the script halt if an exception is thrown by a feature receiver. But what about web-based feature activation? In this case the feature runs in the context of web application, and it is really up to the web application to handle the exception and log it appropriately, so from this prospective it is even more important to not log any errors in your feature receiver.

 

Hold on though, did I not just suggest adding exception handling logic to the base feature receiver classes? Indeed so, but handling an exception really does not necessarily mean *logging* it. In the base class I normally catch any unhandled exception and re-throw a new one, with properly formatted text, or any other contextual information, which can be useful. If you think of what piece of useful information could you add to the exception being re-thrown, start from the feature name. That alone will make your debugging easier!

 

Hope this helps. Good luck feature-receiving!

Leave a Comment
  • You may also need to deal with Farm and Web Application scoped features when casting properties.Feature.Parent

    I've put together a blog post with some of the more esoteric details of how SharePoints Feature Receiver Events work. Thinks like what triggers each event and which account it will run under - not quite as obvious as it first sounds.

    blog.pentalogic.net/.../sharepoint-feature-receivers-events-details