How to Bind to Enums on JSON Objects in ASP.NET MVC 3

NOTE: This has been fixed in ASP.NET MVC 4. If you can upgrade from 3 to 4 then that's all you need to do in order to bind to enums. If you still need the work around for MVC 3, read on.

In ASP.NET MVC 3 the default model binder has been upgraded, allowing you to bind to JSON objects natively. This is great except for one issue. Let's say you have a C# object that contains an enum as one of its properties. When you pass this object to your view using the Json() method to serialize the C# object into JSON, the integer representation of the enum gets passed to the JSON.

When you attempt to pass this object back to your action method and bind to it, you will notice that the enum property has defaulted to the first of all available values in the enum. When parsing the response sent to the server you see that the correct integer value of the enum was indeed passed back to the server.

The issue is with the default model binder. The correct integer value makes it to the model binder but the binder is not coded to map to the integer value of the enum. It correctly binds if the value being passed in is a string containing the named value of the enum. The problem with this is that when you parse a C# object into JSON using the Json() method it sends the integer value as the enum value, not the named value.

The easiest and most transparent fix for this is to override the default model binder and write some custom logic to fix the way it binds enums.

  1. Create a new class, like so.
namespace CustomModelBinders
{
    /// <summary>
    /// Override for DefaultModelBinder in order to implement fixes to its behavior.
    /// This model binder inherits from the default model binder. All this does is override the default one,
    /// check if the property is an enum, if so then use custom binding logic to correctly map the enum. If not,
    /// we simply invoke the base model binder (DefaultModelBinder) and let it continue binding as normal.
    /// </summary>
    public class EnumModelBinder : DefaultModelBinder
    {
        /// <summary>
        /// Fix for the default model binder's failure to decode enum types when binding to JSON.
        /// </summary>
        protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext,
            PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
        {
            var propertyType = propertyDescriptor.PropertyType;
            if (propertyType.IsEnum)
            {
                var providerValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
                if (null != providerValue)
                {
                    var value = providerValue.RawValue;
                    if (null != value)
                    {
                        var valueType = value.GetType();
                        if (!valueType.IsEnum)
                        {
                            return Enum.ToObject(propertyType, value);
                        }
                    }
                }
            }
            return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
        }
    }
}
  1. Then simply register it in your Global.asax file.
protected override void OnApplicationStarted()
{
    base.OnApplicationStarted();

    AreaRegistration.RegisterAllAreas();
    RegisterRoutes(RouteTable.Routes);

    // Register your new model binder
    ModelBinders.Binders.DefaultBinder = new EnumModelBinder();
}

That's it. Enums will now be correctly bound on JSON objects.