我在控制器中有一个方法,该方法接受一个对象作为参数并返回JsonResult。这个对象上的一个属性是一个枚举类型,有三种可能的值。我认为当客户端为该属性传递 int 值时,它会填充枚举类型,但实际上它没有,而是默认为 0 并将枚举设置为可选项中的第一个。
有什么建议吗?
我在控制器中有一个方法,该方法接受一个对象作为参数并返回JsonResult。这个对象上的一个属性是一个枚举类型,有三种可能的值。我认为当客户端为该属性传递 int 值时,它会填充枚举类型,但实际上它没有,而是默认为 0 并将枚举设置为可选项中的第一个。
有什么建议吗?
注意:此问题已经在MVC 4中得到解决。如果升级到MVC 4对您的项目是可行的,那么您只需要这样做就可以开始将枚举绑定到模型。
即便如此,如果您仍然需要在MVC 3中使用,请看以下解决方法:
问题出在MVC中的默认模型绑定程序。正确的整数值传递到了模型绑定程序,但绑定程序没有编码以将其映射到枚举的整数值。如果传递的值为包含枚举命名值的字符串,则它会正确地进行绑定。但问题是,当使用Json()
方法将C#对象解析为JSON时,它将整数值作为枚举值发送,而不是命名值。
最简单和最透明的解决方法是覆盖默认模型绑定程序并编写一些自定义逻辑来修复它绑定枚举的方式。
创建一个新类,如下所示。
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);
}
}
}
然后只需在Global.asax文件中注册它即可。protected override void OnApplicationStarted()
{
base.OnApplicationStarted();
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
// Register your new model binder
ModelBinders.Binders.DefaultBinder = new EnumModelBinder();
}
就这样。现在枚举类型将正确地绑定到JSON对象上。
http://www.codetunnel.com/how-to-bind-to-enums-on-json-objects-in-aspnet-mvc-3
那绑定到模型的钩子属性呢?
public class SomeModel
{
public MyEnum EnumValue { get; set; }
public int BindToThisGuy
{
get { return (int) EnumValue; }
set { EnumValue = (MyEnum)value; }
}
}
namespace MyApp.Enums
{
public enum ATS_Tabs { TabOne = 0, TabTwo = 1, TabThree = 2, TabFour = 3, TabFive = 4 };
public class ModelEnums
{
public static IEnumerable<Type> Types
{
get
{
List<Type> Types = new List<Type>();
Types.Add(typeof(ATS_Tabs));
return Types;
}
}
}
}
接下来,我创建了一个模型绑定器并实现了IModelBinder接口(参考kdawg的评论和链接)。
namespace MyApp.CustomModelBinders
{
public class EnumModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
ValueProviderResult valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
ModelState modelState = new ModelState { Value = valueResult };
object actualValue = null;
try
{
return Enum.ToObject(Type.GetType(bindingContext.ModelType.AssemblyQualifiedName), Convert.ToInt32(valueResult.AttemptedValue));
}
catch (FormatException e)
{
modelState.Errors.Add(e);
}
bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
return actualValue;
}
}
}
为确保valueResult.AttemptedValue的转换不会失败,可以添加一些代码来帮助。
接下来,在Global.asax.cs中循环遍历我创建的枚举类型列表,并为它们添加模型绑定器。
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
foreach (Type type in ModelEnums.Types)
{
ModelBinders.Binders.Add(type, new EnumModelBinder());
}
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
我承认,这不是最直观的方式,但对我来说非常有效。如果有优化建议,请随时告诉我。