Attributes are lousy decorators


Attributes allow developers to provide a mechanism to add metadata to types, assemblies, type members, method parameters, and just about anything else under the sun.  One of the first trends I noticed in my early days of combing the BCL with Reflector was that without fail, no attribute type provided any behavior.  The only information an attribute provided was its type and its properties, but I never found a method that did any work.  Which makes sense, as attributes define metadata, not behavior.

But in ASP.NET MVC, this pattern is broken with the ActionFilterAttribute.  Because a single controller can have many actions, it’s difficult to create a mechanism that executes code before or after any single action and not any other.  Since attributes can be placed on both method and class declarations, this would seem like an ideal candidate to specify before/after behavior on a controller action.

The problem is that attributes aren’t designed to have behavior, and are a lousy implementation of the decorator pattern.  It’s important to remember that developers have little to zero control over when attribute instances are created, which is why you never want an exception to be thrown in an attribute constructor.

Since you don’t have control over the construction, and can’t provide an alternative construction method like an IoC container to do the instantiation, you wind up having to do things like this:

public class BadBadFilterAttribute : ActionFilterAttribute
{
    private readonly ICustomerRepository _customerRepository;

    public BadBadFilterAttribute() : this(ObjectFactory.GetInstance<ICustomerRepository>())
    {
    }

    private BadBadFilterAttribute(ICustomerRepository customerRepository)
    {
        _customerRepository = customerRepository;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // Fetch the customer or something
    }
}

We’re attempting to use StructureMap to locate the dependency for this attribute, a Customer repository.  However, since I don’t control when that constructor is called, I can get some weird behavior and exceptions when doing simple things like reflection.  But since reflection triggers attribute constructors, often I’ll see StructureMap spinning up trying to locate dependencies, when all I wanted to do was inspect the metadata.

Since attributes are the built-in mechanism for adding decorator behavior to an action, you can borrow the MonoRail method of providing action filters.  In addition to the MVC way, MonoRail allows you to simply specify the type of the IFilter, and it will create and execute that filter for you.  This way, you don’t need to create an attribute class, but instead just an implementation of IFilter.  This is easy to do in MVC as well:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class ContainerLocatedActionFilterAttribute : FilterAttribute, IActionFilter
{
    private readonly Type _actionFilter;

    public ContainerLocatedActionFilterAttribute(Type actionFilter)
    {
        _actionFilter = actionFilter;
    }

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var instance = (IActionFilter)ObjectFactory.GetInstance(_actionFilter);
        instance.OnActionExecuting(filterContext);
    }

    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var instance = (IActionFilter)ObjectFactory.GetInstance(_actionFilter);
        instance.OnActionExecuted(filterContext);
    }
}

Our filter can simply be an IActionFilter (or some other interface, at this point, it really doesn’t matter), without worrying about thread safety and other issues with attribute instances.  Although attributes are nice in that they’re declarative and defined at the point of most usefulness (on the action or controller), attributes in general weren’t designed with behavior in mind.  It’s possible to get yourself into threading trouble if you don’t understand how attributes are instantiated and allocated, which usually requires a good read in the CLR via C# book.

There are other ways to provide decorators for actions, which I’ll look at in the next a future post.

Visualizing LINQ expressions in the debugger