Power Up Your Enumerations

One of the patterns I follow a lot is creating enumerations in my code for certain reference data.  I usually have tables in my SQL database to back up this data, essentially just to give myself the benefit of referential integrity.  But it’s a pain to refer to data based on an arbitrary ID in the database, so enumerations are a way to ensure my code isn’t littered with “magic numbers.”

Enumerations by themselves are powerful, but pretty plain.  Since 95% of my development work is in ASP.NET MVC these days, I wanted an easy way to render the options an enumeration provides as a drop-down or radio button list.  I could do this manually, and it’s not a lot of code, but I felt like there needed to be an easier, more generic way to handle things.

So let’s assume you have input model that looks like this:

public class VehicleInputModel {
    public int Year { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }
    public VehicleType VehicleType { get; set; }

    public enum VehicleType {
        Car = 1,
        Truck = 2,
        SUV = 3
    }
}

This is pretty standard stuff.  You’ll have to forgive the contrived example, but it basically works.  I may have two database tables.  One called “Vehicle” and the other called “VehicleType,” and they have a foreign-key relationship between them.  All I’ve done here is make an enumeration that matches my VehicleType table so I can refer to them in code without having to sprinkle ID numbers all over the place.  But what happens when I want to make a form in ASP.NET MVC to create or edit a vehicle?  Some people might reach for the database and grab the data from the reference table to create our drop-down.  I offer an alternative: let’s beef up the enumeration:

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

public enum VehicleType {
    [Display(Name="Car")]
    Car = 1,

    [Display(Name="Truck")]
    Truck = 2,

    [Display(Name="Sport Utility Vehicle (SUV)")]
    SUV = 3
}

Data annotations to the rescue!  You’re already using them all over the place in ASP.NET MVC because of the validation system (at least I hope you are).  Why not put them to work for our enumerations, too?  In this case, I’ve attached some arbitrary metadata to my enumeration that will make displaying data a bit easier.

First, let’s create a generic helper method that will read data from these attributes.  I like to use an extension method for this, and I extend the System.Enum class:

public static string GetAttributeValue<T>(this Enum e,
    Func<T, object> selector) where T : Attribute {

    var output = e.ToString();
    var member = e.GetType().GetMember(output).First();
    var attributes = member.GetCustomAttributes(typeof (T), false);

    if (attributes.Length > 0) {
        var firstAttr = (T)attributes[0];
        var str = selector(firstAttr).ToString();
        output = string.IsNullOrWhiteSpace(str) ? output : str;
    }

    return output;
}

This helper is fairly straightforward.  All I’m doing is finding the custom attributes on the enumeration and then using a simple function to grab any value I want.  If there are no attributes defined, then I just return the default ToString() value.

Because the Html.DropDownListFor() method takes an IEnumerable<SelectListItem>, I’ve written another handy method that converts an enumeration into that type:

public static IEnumerable<SelectListItem> ConvertToSelectList<T>(int? selectedItem = null) {
    return Enum.GetValues(typeof (T)).Cast<T>().Select(x => new SelectListItem {
        Text = (x as Enum).GetAttributeValue<DisplayAttribute>(y => y.Name),
        Value = Convert.ToInt32(x).ToString(),
        Selected = selectedItem.HasValue ? selectedItem == Convert.ToInt32(x) : false
    });
}

This method grabs all the values in an enumeration, then uses the extension method above to read the Name property from the DisplayAttribute on each value.  If there is no DisplayAttribute present, then a simple ToString() is called on each value.  It also takes an optional selectedItem argument that can be used to determine which item in the list should be selected.  This leaves me with a collection of SelectListItem objects where the Text property on each object is determined by the DisplayAttribute.  Pretty sweet, huh?  Now I can extend my VehicleInputModel like so:

public class VehicleInputModel {
    public int Year { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }
    public VehicleType VehicleType { get; set; }
    public IEnumerable<SelectListItem> VehicleTypeSelection { get; set; }

    public VehicleInputModel() {
        VehicleTypeSelection = EnumExtensions.ConvertToSelectList<VehicleType>();
    }

    public enum VehicleType {
        Car = 1,
        Truck = 2,
        SUV = 3
    }
}

And finally, I can render this drop-down list in my view like so:

@Html.DropDownListFor(m => m.VehicleType, Model.VehicleTypeSelection)

I hope you can see how a couple easy, generic methods and the built-in data annotations can make your enumerations a lot beefier.  This can save you some time and database hits in the long run.  I’m not suggesting that you use enumerations for every single reference table in your project, and I know this example is a bit contrived.  My goal is simply to illustrate the power of a few well-placed attributes and a handy method in certain situations that can clean up your controllers and views a bit.  Cheers!

Tags: ,