Decorating with Attributes

September 24, 2014

Blog | Development | Decorating with Attributes
Decorating with Attributes

Often, in the SDLC, you grow tired of looking at the same chunk of code being used in every method. If you’re an MVC guy like me, you might wonder how you can make it more visually appealing and as a result, more functional. Decorate it, if you will. The code would be easier to read and a good deal could be inferred from the decorations. I set out to make it happen and aimed for this:

[PageIsNull]
[IsNotShell]
public override void Process(HttpRequestArgs args)
{...

I started with a few attributes:

public class PageIsNull : Attribute
{
    return Context.Item == null;
}...

The execution failed, so I dug deeper. While you can decorate a MVC action with Authorize, Attributes are a way of storing meta-data and not intended to alter the execution of the current method. But… that’s what I wanted.

With the use of Reflection, Attributes can be accessed on a method and data stored on the "expected" behavior. Then, the Logic can be performed.

The solution? Write a Base class that contains a method to check the attributes and alter the execution accordingly. Below is the result: attributes, pipelines, and base:

Example Attributes:

public abstract class PageCheckAttribute : Attribute
{
    public bool expectedValue;

    /// 
    /// Abstract class to enforce all child attributes to be able to be checked
    /// 
    /// 
    public abstract bool ValidationCheck(IPageCheckService pageCheckService);
}

public class PageIsNull : PageCheckAttribute
{
    public PageIsNull(bool expected)
    {
        expectedValue = expected;
    }

    public override bool ValidationCheck(IPageCheckService pageCheckService)
    {
        return pageCheckService.PageIsNull == expectedValue;
    }
}

public class IsShellOrServiceSite : PageCheckAttribute
{
    public IsShellOrServiceSite(bool expected)
    {
        expectedValue = expected;
    }

    public override bool ValidationCheck(IPageCheckService pageCheckService)
    {
        return pageCheckService.IsShellOrServiceSite == expectedValue;
    }
}

Decorated Pipeline Excerpt:

public class CustomNotFound : PipelineBase
{
    [PageIsNull(true)]//want page to be NULL
    [IsShellOrServiceSite(false)]//do not want to execute on Shell or Service page
    [IsNormalSitePage(true)]//want to run on normal Site page
    public override void Execute()
    {...

PipelineBase:

public abstract class PipelineBase : HttpRequestProcessor
{
    public HttpRequestArgs args { get; set; }

    /// 
    /// Overridden HttpRequestProcessor method
    /// 
    /// 
    public override void Process(HttpRequestArgs args)
    {
        //page validate via Attributes
        if(IsValid())
        {
            Execute();
        }

    }

    public abstract void Execute();

    public bool IsValid()
    {
        IPageCheckService pageCheckService = LoadPageCheckService();

        bool retVal = true;
        var callingType = this.GetType();
        var allAttributes = callingType.GetMethod("Execute").GetCustomAttributes(typeof(PageCheckAttribute), true).ToArray();
        if (allAttributes.Count() > 0)
        {
            foreach (PageCheckAttribute pageCheck in allAttributes)
            {
                if (!pageCheck.ValidationCheck(pageCheckService))
                {
                    retVal = false;
                    break;
                }
            }
        }
        return retVal;
    }

    /// 
    /// Page check service performs logic to determine the current status of the page: Is null, Is shell, etc.
    /// 
    /// 
    private IPageCheckService LoadPageCheckService()
    {
        IPageCheckService pageCheckService = IoC.GetInstance();
        return pageCheckService;
    }
}

*Note: this example introduces Reflection to Pipelines which may or may not be executed literally thousands of times. Be cognisant of performance.

Have you used Attributes to impact Sitecore behavior? How do you streamline your backend code in Sitecore? Tweet us @GeekHive.

Steve VandenBush

Technical Lead
Tags
  • Patterns & Practices
  • Tutorial

Recent Work

Check out what else we've been working on