Chev Chev - 2 days ago 3
C# Question

Model Binding to Enums in ASP.NET MVC 3

I have a method in my controller that accepts an object as an argument and returns a JsonResult. One of the properties on this object is an enum with three possible values. I assumed that when the client passed in an int for that property it would populate the enum, but it doesn't, it defaults to 0 and the enum is set to the first of it's possible selections.

Any suggestions?

Answer

NOTE: This has been resolved in MVC 4. If upgrading to MVC 4 is a viable option for your project then that is all you have to do in order to begin model-binding to enums.

That said, here is the workaround for MVC 3 if you still need it.


The issue is with the default model binder in MVC. 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);
            }
        }
    }
    
  2. 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.

http://www.codetunnel.com/how-to-bind-to-enums-on-json-objects-in-aspnet-mvc-3