How To: Write Custom Sitecore Disablers

What if you want the Sitecore EventDisabler to only disable some of your events, but not all? This is one of the many reasons to write your own disabler, and it’s actually pretty easy.

A while back, I was curious as to how the disablers were implemented, so I decompiled Sitecore.Kernel to take a look. What I found is that it implements the Switcher class (which is incredibly simple to implement), so I figured that I would write a few disablers of my own and share that process with you.

Jump to TL;DR →

But first, why would you want to write your own disabler?

If you’ve ever asked yourself any of these questions, then you may want to consider writing your own disabler:

  • What if I want the EventDisabler to only disable some of my events but not all?
  • What if I want the EventDisabler to only disable only my custom event handlers?
  • What if I want to disable some of my custom logic across multiple methods in the same request?
  • What if I want to prevent a custom pipeline processor from running when I call a particular piece of code?
  • What if I want to disable some of my custom rules when I call a particular piece of code?
  • And many more!

What is the Sitecore Switcher class?

The Switcher class is one of my favorite and IMHO one of the most underutilized features of the Sitecore API.

Sitecore has built-in disablers, like the SecurityDisabler and the EventDisabler classes, which all inherit the Sitecore-provided Switcher class. This class is an IDisposable that can be used to create disablers for use in both pipeline processors, event handlers and more without issue or additional customizations.

The Switcher class is used for more than just Sitecore’s disablers. Sitecore’s various ____Switcher classes, like the SiteContextSwitcher, the LanguageSwitcher and more all inherit from the Switcher class.

The Switcher class works by reading from and moving objects in the Context.Items stack, and “switches” them back when the instance (in our case: the disabler instance) is disposed of.

How To: Write a Custom Disabler in One (or Three) Easy Step(s):

So, let’s get going! The first time you write a disabler, you must write a base class and an enum. For subsequent disablers, you can skip these two steps and reuse the types that we already created.

Step 1 (first time, only): Create an enum that denotes the state of the disabler

Since the disabler shouldn’t always be active, you need an enum to define the possible states that the disabler can have. As such, create the following, for defining the disabler’s available states:

    public enum DisablerState
    {
        Disabled,
        Enabled
    }

Notice that Disabled is listed before Enabled. This is intentional and will be necessary in order for our disabler to run properly. I will go into this in greater detail after we create our base class.

Step 2 (first time, only): Create a base class for all disablers to implement

In order to account for a custom disabler type in your code you will need to call a static method or property to determine if a disabler of your specific type is active. As such, if you give your new type a name like “Disabler” then your code will likely be confusing, since that name is just too generic and will not be meaningful enough to help identify what it is supposed to disable. As such, I use this class as a base type for all of my disablers, and mandate that developers make new and more meaningfully named disabler classes that inherit this type by marking it abstract. I cannot recommend this strongly enough, as it will keep you from winding up in disabler hell, which I will describe in greater detail, later in the post.

The Switcher class is easy enough to implement, but we do need to add a little custom logic for enabling and disabling it. This logic should be shared by all disablers, it it makes sense to create a base class. Let’s start by writing the base class’ definition and inheriting the Switcher class:

    public abstract class Disabler<TSwitchType> : Switcher<DisablerState, TSwitchType>
    {
        public Disabler() : base(DisablerState.Enabled) { } 

        public static bool IsActive
            => CurrentValue == DisablerState.Enabled;
    }

Before we move onto the next step, there are a few things to note about this class in order for it to really make sense:

  1. Notice that the base class is generic. The type parameter TSwitchType should be passed the type of the actual disabler, so that the Switcher knows what to look for when trying to retrieve the disabler from the stack. Confused by this? Don’t Panic! This will likely make more sense after Step 3, once we have implemented our first disabler that derives from our base class.
  2. Notice that you are passing DisablerState.Enabled to the base constructor. The Switcher class that you are inheriting is actually the generic Switcher<TValue,TSwitchType> class, which holds a “current value” of type TValue that you are specifying to be your new enum, DisablerState. This tells the Switcher that the value it will be holding onto is a DisablerState.
  3. The static property IsActive checks to see if the CurrentValue is the “enabled state.” This is the property that you will use to determine if there is an active disabler of a particular type, in order to determine whether a piece of logic should be skipped (a.k.a. “disabled”).
  4. Do you remember when we created the DisablerState type, and I said that it was important to list Disabled before Enabled? If you did not do that, then your disabler will always be Enabled. This is because when the disabler is “disposed of,” it removes its “current value” from the stack. If you try to read that value again from the stack (i.e. check to see if the disabler is active again), the default value for the DisablerState enum type will be returned. You need the default value of DisablerState to be Disabled, since you want the disabler to be “inactive” after it has been disposed of. Thus, since the default value of any enum type is the first value listed, you must list Disabled first.


Step 3: Create a disabler type that implements our base class

Now that you have all of the reusable base types, it is time to start writing the new disablers. Your disablers should look something like the following:

    public sealed class MyCustomDisabler : Disabler<MyCustomDisabler>
    {
    }

That’s it! No extra members or logic!

Be sure to take note of the following, as they are important to understanding your new disabler:

  1. When you inherit the base Disabler<TSwitchType> class, note that you’re passing the type that we’re defining as the TSwitchType. Remember, this is so that the disabler is able to find the object of the correct type in the stack. As such, if your disabler is of type MyCustomDisabler then you want to find a disabler of type MyCustomDisabler in the stack. Thus, we pass MyCustomDisabler in for the TSwitchType.
  2. Even if you removed the sealed access modifier from the type signature, any class that inherits this one will inherently not work. That’s because the disabler will still be looking for a Switcher object of type MyCustomDisabler in the stack. For this reason, I prefer to mark my disabler classes sealed to make them more readable and force developers to create new types instead of trying to extend an existing disbaler type.

Suggested Naming Conventions

You can name your disablers however you like, but you want to make sure that you come up with a consistent naming convention, so as to avoid ending up in disabler hell.

What leads to “Disabler Hell” and what are its symptoms?

If your disabler names are too generic and your developers cannot figure out what your disablers are actually supposed to disable, then you may quickly find yourself in “Disabler hell”. Symptoms may include, but are not limited to:

  • Developers under-using disablers; creating multiple disablers to disable the same thing

    • Can lead to code duplication, inconsistencies, extra disabler checks, and more
    • Can lead to forgetting to account for some disablers meant to be used for a particular piece of logic
  • Developers over-using disablers; using a single disabler for more things than it is meant for

    • Can lead to reduced code readability
    • Can lead to bugs caused by a disabler being active at unintended times, as it may be enabled by for two or more completely separate uses

Naming Conventions (to avoid Disabler Hell)

I tend to use the following syntax for my disabler names:

[pipeline|event|feature|etc][Purpose]Disabler

Splitting my naming convention in parts:

[event_name|feature|etc][Event|Processor|Logic|etc]

Prefix: Purpose

The purpose is what disabler is meant to be used for.

If creating a disabler that’s meant to disable logic is triggered when an item is deleted, the value may be “ItemDeleted”. If creating a disabler that is meant to disable user registration code, the value may be “UserRegistration”.

Suffix: Location

The location is where the disabler is meant to be accounted for (i.e. the location of the logic that actually gets disabled).

While some may argue that including the location in the disabler’s name may limit reusability and/or readability, I would argue that ensuring that your developers do not over-use your disablers (i.e. using them to disable radically different pieces of code) is more important. Remember: disablers are very easy to make; no one is going to look at you funny for having a few extra disablers that look like they do something similar, so long as you are consistent in how you use them.

I use the following suffixes to identify the location where my disabler should be used:

  • LogicDisabler: multi-location; disables blocks of code, events, pipeline processors, etc.
  • CodeDisabler: disables custom code, only
  • RuleDisabler: disables custom rules (if making very granular disablers, this can be combined with other suffixes, e.g. “EventRuleDisabler” or “RuleLogicDisabler”)
  • EventDisabler: disables custom event handlers
  • ProcessorDisabler: disables custom pipeline processors
  • RenderingDisabler: disables a custom rendering (hides it via check in controller action; less frequently used)
  • PipelineDisabler: disables a custom pipeline

Note that all of my disabler suffixes end with “Disabler”. This way, I know that the type represents a water balloon 🙂

Disabler Examples:

For the purposes of this post, we’ll implement a few different disablers that will disable custom logic that’s automatically called when an item is saved.

While we could create a single disabler that will work for both processors and event handlers, for maximum control, we want a disabler that’s able to disable code in each of the following locations:

  1. Both event handlers and pipeline processors
  2. Event handlers, only
  3. Pipeline processors, only

Assumptions

The solution includes custom logic that is triggered on save via an item:saved event handler and other custom logic that is triggered on save via a processor for the new <saveItem> item provider pipeline.

Example 1: Disabler for both custom item:saved event handlers and <saveItem> processors

Since we want to use our disabler in multiple locations, we will suffix its name with “LogicDisabler”.

    public sealed class SaveItemLogicDisabler : Disabler<SaveItemLogicDisabler>
    {
    }

Now that we have our new disabler, it’s time to add it to our item:saved event handler and our <saveItem> processor.

Processor:

    public override void Process(SaveItemArgs args)
    {
        // if a disabler is active or the pipeline is aborted, go no further
        if (SaveItemLogicDisabler.IsActive || args.Aborted)
        {
            return;
        } 

        Assert.IsNotNull(args.Item, "Item is null"); Assert.IsNotNull(args.FallbackProvider, "FallbackProvider is null");

        args.ProcessorItem = args.Item;
        args.Result = args.FallbackProvider.SaveItem(args.Item);

        // additional processor logic here...
    }

Event Handler:

    private void OnItemSaved(object sender, EventArgs args)
    {
        // if a disabler is active, go no further
        if (SaveItemLogicDisabler.IsActive)
        {
            return;
        }

        Assert.ArgumentNotNull(sender, nameof(sender));
        Assert.ArgumentNotNull(args, nameof(args));

        // additional handler logic here...
    }

Example 2: Disabler for custom item:saved event handlers

First, we create our new disabler:

    public sealed class SaveItemEventDisabler : Disabler<SaveItemEventDisabler>
    {
    }

Now that we have our new “event disabler,” it’s time to update our existing event handler to look for either an active SaveItemLogicDisabler or a SaveItemEventDisabler:

    private void OnItemSaved(object sender, EventArgs args)
    {
        // if a disabler is active, go no further
        if (SaveItemLogicDisabler.IsActive || SaveItemEventDisabler.IsActive)
        {
            return;
        }

        Assert.ArgumentNotNull(sender, nameof(sender));
        Assert.ArgumentNotNull(args, nameof(args));

        // additional handler logic here...
    }

Example 3: Disabler for custom <saveItem> processors

First, we create our new disabler:

    public sealed class SaveItemProcessorDisabler : Disabler<SaveItemProcessorDisabler>
    {
    }

Now that we have our new “event disabler,” it’s time to update our existing event handler to look for either an active SaveItemLogicDisabler or a SaveItemEventDisabler:

    public override void Process(SaveItemArgs args)
    {
        // if a disabler is active, go no further
        if (SaveItemLogicDisabler.IsActive || SaveItemProcessorDisabler.IsActive)
        {
            return;
        }

        if (args.Aborted)
        {
            return;
        }

        Assert.IsNotNull(args.Item, "Item is null"); Assert.IsNotNull(args.FallbackProvider, "FallbackProvider is null"); 

        args.ProcessorItem = args.Item;
        args.Result = args.FallbackProvider.SaveItem(args.Item);

        // additional processor logic here...
    }

Example 4: Initializing the disabler

We’ll initialize our custom disablers in many locations. The following shows how we can disable our event handlers, processors or both by wrapping our logic in one or more of our new disablers:

Disable custom logic for both event handlers and processors:

    ...

    using (new SaveItemLogicDisabler())
    using (new SecurityDisabler())
    {
        item.Editing.BeginEdit();
        ...
        item.Editing.EndEdit();
    }

Disable custom logic for both event handlers and processors:

    ...

    using (new SaveItemEventDisabler())
    using (new SaveItemProcessorDisabler())
    using (new SecurityDisabler())
    {
        item.Editing.BeginEdit();
        ...
        item.Editing.EndEdit();
    }

Disable custom logic for event handlers, only:

    ...

    using (new SaveItemEventDisabler())
    using (new SecurityDisabler())
    {
        item.Editing.BeginEdit();
        ...
        item.Editing.EndEdit();
    }

Disable custom logic for processor, only:

    ...

    using (new SaveItemProcessorDisabler())
    using (new SecurityDisabler())
    {
        item.Editing.BeginEdit();
        ...
        item.Editing.EndEdit();
    }

TL;DR Implementation:

All you need to do is create the following:

    public enum DisablerState
    {
        Disabled,
        Enabled
    }

    public abstract class Disabler<TSwitchType> : Switcher<DisablerState, TSwitchType>
    {
        public Disabler() : base(DisablerState.Enabled) { } 

        public static bool IsActive
            => CurrentValue == DisablerState.Enabled;
    }

Then you can make a disabler by creating a new derived class:

    public sealed class MyCustomDisabler : Disabler<MyCustomDisabler>
    {
    }

Example:

Here is a new disabler to use in custom item:saved event handlers and custom processors for the new <saveItem> pipeline.

    public sealed class SaveItemLogicDisabler : Disabler<SaveItemLogicDisabler>
    {
    }

Which you can use like so:

    ...
    using (new SaveItemLogicDisabler())
    using (new SecurityDisabler())
    {
        item.Editing.BeginEdit();
        ...
        item.Editing.EndEdit();
    }

« Prev Article
Next Article »