Modello Binding to Enum in ASP.NET MVC 3

Ho un metodo nel mio controller che accetta un object come argomento e restituisce un JsonResult . Una delle proprietà su questo object è un enum con tre valori possibili. Supponevo che, quando il client passava in un int per quella proprietà, compilasse l’enum, ma non lo fa, ha come valore predefinito 0 e l’enum è impostato sulla prima delle possibili selezioni.

Eventuali suggerimenti?

NOTA: questo è stato risolto in MVC 4. Se l’aggiornamento a MVC 4 è un’opzione valida per il tuo progetto, allora questo è tutto ciò che devi fare per iniziare l’associazione del modello alle enumerazioni.

Detto questo, ecco la soluzione alternativa per MVC 3 se ne hai ancora bisogno.


Il problema è con il modello di modello predefinito in MVC. Il valore intero corretto lo rende al raccoglitore del modello, ma il raccoglitore non è codificato per mappare il valore intero dell’enumerazione. Lega correttamente se il valore passato è una stringa che contiene il valore denominato dell’enum. Il problema con questo è che quando si analizza un object C # in JSON utilizzando il metodo Json() , esso invia il valore intero come valore enum, non il valore denominato.

La soluzione più semplice e più trasparente per questo è sostituire il modello predefinito di raccoglitore e scrivere una logica personalizzata per risolvere il modo in cui lega enumerazioni.

  1. Crea una nuova class, in questo modo.

     namespace CustomModelBinders { ///  /// 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. ///  public class EnumModelBinder : DefaultModelBinder { ///  /// Fix for the default model binder's failure to decode enum types when binding to JSON. ///  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. Quindi registralo semplicemente nel tuo file Global.asax.

     protected override void OnApplicationStarted() { base.OnApplicationStarted(); AreaRegistration.RegisterAllAreas(); RegisterRoutes(RouteTable.Routes); // Register your new model binder ModelBinders.Binders.DefaultBinder = new EnumModelBinder(); } 

Questo è tutto. Le enumerazioni verranno ora correttamente associate agli oggetti JSON.

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

Che dire del binding a una proprietà hook sul tuo modello?

 public class SomeModel { public MyEnum EnumValue { get; set; } public int BindToThisGuy { get { return (int) EnumValue; } set { EnumValue = (MyEnum)value; } } } 

Ok ragazzi. Ho cercato alcuni modi per farlo perché ero stanco di scrivere un lavoro stupido per superare questa carenza nel framework .Net. Sulla base di un paio di thread, ho composto la seguente soluzione.

Disclaimer, questa non è una soluzione totalmente automatizzata, quindi non funzionerà per tutti. Data la mia implementazione, funziona. Forse la mia strada aiuterà qualcun altro a progettare qualcosa che funzioni per loro.

In primo luogo, ho creato un ripostiglio enum. Le enumerazioni non devono risiedere qui, ma dovrebbero essere visibili dal repository.

Nel repository, ho creato una class e una proprietà statica pubblica per esporre un elenco di tipi enum.

 namespace MyApp.Enums { public enum ATS_Tabs { TabOne = 0, TabTwo = 1, TabThree = 2, TabFour = 3, TabFive = 4 }; public class ModelEnums { public static IEnumerable Types { get { List Types = new List(); Types.Add(typeof(ATS_Tabs)); return Types; } } } } 

Successivamente, ho creato un raccoglitore di modelli e implementato l’interfaccia di IModelBinder (vedi il commento e il collegamento di 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; } } } 

Potrebbe essere utile aggiungere del codice per garantire che la conversione di valueResult.AttemptedValue non abbia esito negativo.

Successivamente, ho fatto un ciclo attraverso l’elenco dei tipi di enum che ho creato sopra e ho aggiunto dei raccoglitori di modelli per loro (… in 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); } 

Lo ammetto, non è il modo più intuitivo, ma funziona benissimo per me. Sentiti libero di farmi sapere se posso ottimizzare questo.