Связывание модели с перечислениями в ASP.NET MVC 3 - PullRequest
47 голосов
/ 19 мая 2011

В моем контроллере есть метод, который принимает объект в качестве аргумента и возвращает JsonResult .Одним из свойств этого объекта является перечисление с тремя возможными значениями.Я предположил, что когда клиент передает int для этого свойства, он заполняет перечисление, но это не так, по умолчанию он равен 0, а перечисление устанавливается на первый из возможных вариантов выбора.

Любые предложения

Ответы [ 3 ]

70 голосов
/ 23 мая 2011

ПРИМЕЧАНИЕ: это было решено в MVC 4. Если обновление до MVC 4 является жизнеспособным вариантом для вашего проекта, то это все, что вам нужно сделать, чтобы начать привязку модели к перечислениям.

Тем не менее, вот обходной путь для MVC 3, если он вам все еще нужен.


Проблема связана с привязкой модели по умолчанию в MVC.Правильное целочисленное значение попадает в связыватель модели, но связыватель не кодируется для сопоставления с целочисленным значением перечисления.Он правильно связывается, если передаваемое значение является строкой, содержащей именованное значение перечисления.Проблема в том, что когда вы анализируете объект C # в JSON с помощью метода Json(), он отправляет целочисленное значение в качестве значения перечисления, а не именованное значение.

Самое простое и прозрачное исправление для этого -переопределить связыватель модели по умолчанию и написать некоторую настраиваемую логику, чтобы исправить способ связывания перечислений.

  1. Создайте новый класс, например:

    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. Затем просто зарегистрируйте его в своем файле 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

15 голосов
/ 19 мая 2011

Как насчет привязки к свойству hook в вашей модели?

public class SomeModel
{
   public MyEnum EnumValue { get; set; }
   public int BindToThisGuy
   {
      get { return (int) EnumValue; }
      set { EnumValue = (MyEnum)value; }
   }
}
3 голосов
/ 31 января 2012

Хорошо, ребята. Я искал несколько способов сделать это, потому что устал от написания глупой работы, чтобы обойти этот недостаток в среде .Net. Основываясь на нескольких потоках, я составил следующее решение.

Отказ от ответственности, это не полностью автоматизированное решение, поэтому оно не будет работать для всех. Учитывая мою реализацию, это работает. Может быть, мой путь поможет кому-то спроектировать что-то, что будет работать для них.

Сначала я создал репозиторий enum. Перечисления не должны находиться здесь, но они должны быть видны из хранилища.

В репозитории я создал класс и открытое статическое свойство для предоставления списка типов перечислений.

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);
    }

Признаюсь, это не самый интуитивно понятный способ, но он прекрасно работает для меня. Не стесняйтесь, дайте мне знать, если я могу оптимизировать это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...