Fun with Attribute Programming


Attribute Programming is a very powerful technique. You would have encountered Attributes in .NET framework, e.g. [Serializable] attribute. It’s particularly useful for cutting across concerns such as Logging, Authentication, Authorization, Caching and Exception Handling. Typically in OO programming we structure our code in layers, and often find that we tend to repeat code to perform logging and exception handling across these layers. This is where cross-cutting is used to cut across these layers and helps to encapsulate and separate our concerns (typically known as Aspect Oriented Programming). If you’re keen on AOP, have a look at the Unity Application Block.

With all that said, I’m going to talk about a different use of Attribute Programming. I find that attributes are a great way to describe an object, in the form of meta-data. In my previous post, I talked about using System.Activator to instiantiate objects via the Factory Method Pattern. I’m going to expand on this example by using attributes as meta-data to decorate my enums to add additional behaviour to my factory-created user-controls.

Let’s define our attribute class.

[AttributeUsage(AttributeTargets.Field)]
public class ControlMetaData : Attribute
{
    public ControlMetaData(string name) : this()
    {
        this.Name = name;
    }

    public ControlMetaData()
    {
        this.Name = Guid.NewGuid().ToString();
        this.Dock = DockStyle.Fill;
        this.Enabled = true;
        this.Visible = true;
    }

    public string Name { get; private set; }
    public DockStyle Dock { get; set; }
    public bool Enabled { get; set; }
    public bool Visible { get; set; }

}

This attribute class provides meta-data about various aspects of a WinForms usercontrol, namely it’s Name, DockStyle, Visibility and if it’s enabled/disabled. With that, we can now decorate our enum members using that custom attribute.

public enum Screens
{
    [ControlMetaData("ucMainMenu", Dock = DockStyle.Fill, Enabled = true, Visible = true)]
    MainMenu = 0,
    [ControlMetaData("ucProductDetails", Dock = DockStyle.Fill, Enabled = false, Visible = true)]
    ProductDetails = 1
}

As you can see, we have provided a “default” meta-data behavior of each user control with data regarding it’s DockStyle, etc. Moving on, here’s a recap of our IControl interface and ControlFactory implementation.

public interface IControl
{
    DockStyle Dock { get; set; }
    bool Enabled { get; set; }
    bool Visible { get; set; }
    string Name { get; set; }
}

public static class ControlFactory
{
    public static IControl CreateControl(Screens screen)
    {
        string assemblyName = Assembly.GetExecutingAssembly().GetName().Name;
        IControl control = Activator.CreateInstance(null, string.Format("{0}.{1}", assemblyName, screen)).Unwrap() as IControl;
        if (control == null)
        {
            // or you can throw an Exception here.
            control = new NullControl();
        }
        ConfigureControl(control, screen);
        return control;
    }
}

In our CreateControl factory method, we added a new ConfigureControl method (Line 20) that is responsible for adding the custom behavior of the control by pulling out the values from the ControlMetadata attribute…like so.

private static void ConfigureControl(IControl control, Screens screen)
{
    FieldInfo fInfo = typeof(Screens).GetField(screen.ToString());
    if (fInfo != null)
    {
        object[] attributes = fInfo.GetCustomAttributes(typeof(ControlMetaData), false);
        if (attributes != null && attributes.Length > 0)
        {
            ControlMetaData metadata = (ControlMetaData)attributes[0];
            control.Name = metadata.Name;
            control.Dock = metadata.Dock;
            control.Enabled = metadata.Enabled;
            control.Visible = metadata.Visible;
        }
    }
}

During each call to create a user control via the factory method, we set the default behavior of each control via the attribute’s meta-data. In this way, we can set different behaviors for every user control, as well as encapsulated the code to do that in one spot. This allows for easy configuration and better code maintainability.

This example might not be very appropriate for WinForms, but have a think about using it with ASP.NET, when you need to load user controls dynamically via LoadControl method. For example, we have a URL like so…http://servername.com/control.aspx?id=1. Imagine for a moment that each id (query string) matches one ASP user control, and we have to use LoadControl method which requires a virtual path of the control’s location. You don’t want to write a huge “switch” statement to load each control dynamically, right?!?!?

public enum ASPControls
{
    [AspControlMetaData(VirtualPath="~/Controls/OrderDetails.ascx")]
    OrderDetails= 1,
    [AspControlMetaData(VirtualPath="~/Controls/ProductDetails.ascx")]
    ProductDetails = 2
}

We can map the query string value (control.aspx?id=1) to the ASPControls enum value and with the same approach, you can now retrieve the VirtualPath property of the attribute and load any control dynamically with just a few lines of code. I’ve used this approach in the past with very great success.

I hope this post has opened up more windows/ideas for you to come up with more interesting usages of attributes. Happy coding.

Share this post:
Advertisements

One Response to “Fun with Attribute Programming”


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: