Los Techies : Blogs about software and anything tech!

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.

Kick It on DotNetKicks.com
Posted Dec 08 2008, 11:07 PM by bogardj
Filed under:

Comments

ASP.NET MVC Archived Blog Posts, Page 1 wrote ASP.NET MVC Archived Blog Posts, Page 1
on 12-09-2008 1:06 AM

Pingback from  ASP.NET MVC Archived Blog Posts, Page 1

Nigel Sampson wrote re: Attributes are lousy decorators
on 12-09-2008 1:11 AM

The other place I've noticed this (attributes providing more than data) is the ValidationAttribute in Dynamic Data. I'm not a fan of this either since it's difficult to reuse any code stored in these attribute, especially complex validation (which certainly shouldn't be in one).

Mogens Heller Grabe wrote re: Attributes are lousy decorators
on 12-09-2008 4:11 AM

This is a great example on why it's hard to create a framework. At first something seems like a cool and easy solution, but when people start using it, stuff like this just happens.

Makes you wonder why Microsoft bother building MVC, the Entity Framework etc. when there are truly mature solutions like MonoRail and NHibernate around.

James Brechtel wrote re: Attributes are lousy decorators
on 12-09-2008 8:03 AM

Why not lazy load the ICustomerRepository on the BadBadFilterAttribute via a property?

bogardj wrote re: Attributes are lousy decorators
on 12-09-2008 8:56 AM

@James

Because then we're not using IoC.  We want our dependencies explicit, and a lazy-loaded property makes it opaque or appear optional.

Dew Drop - December 9, 2008 | Alvin Ashcraft's Morning Dew wrote Dew Drop - December 9, 2008 | Alvin Ashcraft's Morning Dew
on 12-09-2008 9:10 AM

Pingback from  Dew Drop - December 9, 2008 | Alvin Ashcraft's Morning Dew

Arjan`s World » LINKBLOG for December 9, 2008 wrote Arjan`s World &raquo; LINKBLOG for December 9, 2008
on 12-09-2008 2:24 PM

Pingback from  Arjan`s World    » LINKBLOG for December 9, 2008

Reflective Perspective - Chris Alcock » The Morning Brew #241 wrote Reflective Perspective - Chris Alcock &raquo; The Morning Brew #241
on 12-10-2008 2:29 AM

Pingback from  Reflective Perspective - Chris Alcock  » The Morning Brew #241

Rick Minerich's Development Wonderland wrote Discoveries This Week 01/09/2008
on 01-09-2009 10:38 AM

It’s been a very exiting week.  I actually had more things to post than time would allow me to write

VS2010学习 wrote What is a Controllerless Action?
on 06-30-2009 3:43 AM

In a typical ASP.MVC project, a controller maps to a class and and action maps to a method on that class

Writing Jobs wrote re: Attributes are lousy decorators
on 02-24-2010 3:16 AM

The other place I've noticed this (attributes providing more than data) is the ValidationAttribute in Dynamic Data. I'm not a fan of this either since it's difficult to reuse any code stored in these attribute, especially complex validation (which certainly shouldn't be in one).

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

Enter the numbers above:
Copyright Los Techies 2008, 2009. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems