Пользовательское сообщение об ошибках ASP.NET для поля перечисления модели

Я разрабатываю веб-сайт, построенный на EntityFrameworkCore и ориентированный на ASP.NET Core 2.1. Я хочу указать сообщение об ошибке для поля перечисления в моей модели следующим образом:

[Required(ErrorMessage = "Select an item from the list.")]
public MyEnum MyEnum { get; set; }

Тем не менее, стандартное сообщение все еще генерируется: The value '0' is invalid. Проблема заключается в том, что тип Enum проверяется до оценки любого моего кода. Представленные здесь два подхода (https://www.codeproject.com/Articles/1204077/ASP-NET-Core-MVC-Model-Validation), либо создавая класс, наследуемый от ValidationAttribute, либо наследуя модель от IValidatableObject, оба страдают от этого.

Я нашел обходной путь: объявите поле как int, а затем используйте собственный атрибут проверки:

[EnumCheck(typeof(MyEnum), ErrorMessage = "Select an item form the list.")]
public int MyEnum { get; set; }

... а затем подкласс от ValidationAttribute:

sealed public class EnumCheck : ValidationAttribute
{
    readonly Type t_;

    public EnumCheck(Type t)
    {
        t_ = t;
    }

    public override bool IsValid(object value)
    {
        return Enum.IsDefined(t_, value);
    }
}

У этого подхода есть некоторые недостатки, так как теперь мне нужно привести поле к типу Enum во многих местах, где он используется.

Есть ли способ предоставить ErrorMessage для типов полей Enum?

ОБНОВЛЕНИЕ

Ниже приведен минимальный пример (больше не используется подкласс EnumCheck из ValidationAttribute, а используется EnumDataType, упомянутый @PéterCsajtai):

Модель

namespace web.Models
{
    public enum Day
    {
        Sunday = 1,
        Monday,
        Tuesday,
        Wednesday,
        Thursday,
        Friday,
        Saturday
    }

    public class Form
    {
        [EnumDataType(typeof(Day), ErrorMessage = "Select an item from the list.")]
        public Day Day { get; set; }
    }
}

Контроллер

namespace web.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        public IActionResult Save(Form model)
        {
            if(!ModelState.IsValid)
            {
                return View("Index");
            }

            return View("Index", model);
        }
    }
}

Вид

<form asp-controller="Home">
    <div asp-validation-summary="All" class="text-danger"></div>
    <fieldset>
        <label asp-for="@Model.Day"></label>
        <select asp-for="@Model.Day" asp-items="Html.GetEnumSelectList<Day>()">
            <option value="">Select...</option>
        </select>
        @Html.ValidationMessageFor(m => m.Day)
        <span asp-validation-for="@Model.Day" class="text-danger"></span>
    </fieldset>
    <fieldset>
        <input type="submit" asp-action="Save" />
    </fieldset>
</form>

И вывод после сообщения формы:

вывод формы


person Matthew Peltzer    schedule 31.10.2018    source источник
comment
Enum — это целое число. Возможно, 0 - это ваша проблема. Вы пытались установить первое перечисление в 1?   -  person Manta    schedule 01.11.2018
comment
@Манта Да, есть. Собственно, так я впервые заметил проблему. MyEnum заполняется из поля выбора, которое сначала содержало только каждый элемент из перечисления. Затем я решил, что мне не нужна опция «Выбрать один…» в верхней части списка, и установил для нее значение 0.   -  person Matthew Peltzer    schedule 01.11.2018
comment
Похоже, вы ищете это: stackoverflow.com/q/14381564/125981   -  person Mark Schultheiss    schedule 01.11.2018


Ответы (2)


  • В исходном случае [Required(ErrorMessage = "Select an item from the list.")] вы устанавливаете сообщение, которое будет отображаться, если MyEnum отсутствует. Но, как и все ValueTypes, он никогда не может отсутствовать, поэтому он никогда не вызовет эту проверку. Решением для этого является обнуляемый ValueType.

  • Ваша вторая попытка по-прежнему не работает, потому что ошибка связывания модели — «Можно ли пустое значение преобразовать в Day? Нет, нельзя». срабатывает до того, как срабатывает ваша проверка.

Проверка для Type предполагает, что у вас есть instance из этого Type для проверки. Способ, которым Aspnetcore превращает публикацию формы в это типизированное значение, называется привязкой модели. Если опубликованное значение не может быть привязано к модели — например, если вы отправляете «boo» в свойство, объявленное как int, или пустую строку в Enum — тогда проверка даже не начинается. . Вместо этого отображается ошибка связывания модели.

Простое решение

  • Используйте обнуляемое перечисление, Day?, чтобы привязка модели к пробелу выполнялась успешно (пусто преобразуется в null).
  • Используйте [Required()], чтобы это значение null не прошло проверку.

Вывод: измените форму на:

public class Form
{
    [Required(ErrorMessage = "Select an item from the list.")]
    public Day? Day { get; set; }
}

И тогда он будет работать так, как вы ожидаете.

Ссылка: Проверка модели в AspNet Core MVC

Обратите внимание, что в отличие от других ValidationAttributes атрибут документация для EnumDataType, хотя он и наследуется от ValidationAttribute, не дает примера его использования для проверки. Вместо этого пример использования его для метаданных.

person Chris F Carroll    schedule 24.11.2018

Я думаю, вы ищете EnumDataTypeAttribute:

[EnumDataType(typeof(MyEnum), ErrorMessage = "Select an item form the list.")]
public MyEnum MyEnum { get; set; }
person Péter Csajtai    schedule 31.10.2018
comment
Это полезно, кажется, он делает именно то, что делает мой класс атрибутов EnumCheck, поэтому я могу его удалить. Однако, если я объявлю поле как public MyEnum MyEnum { get; set;}, я получу сообщение The value '0' is invalid, а не указанное ErrorMessage. Мне все еще нужно объявить поле как int. - person Matthew Peltzer; 01.11.2018
comment
Это странно, я получил правильное сообщение о проверке в моем примере проекта, как именно вы делаете проверку? - person Péter Csajtai; 01.11.2018
comment
Именно так, как у вас. Я пошел и сделал новую душу/проект в качестве минимального примера с теми же результатами. Возможно, мне нужен другой способ отображения ошибки? Я пробовал оба: @Html.ValidationMessageFor(m => m.MyEnum) и <span asp-validation-for="@Model.MyEnum" class="text-danger"></span> - person Matthew Peltzer; 24.11.2018
comment
Я думаю, что этот план никогда не сработает. Если опубликованное значение не может быть преобразовано в перечисление, то ValidationAttributes никогда не будут достигнуты. Ошибка возникает этапом раньше, в привязке модели. Если опубликованное значение может быть преобразовано в перечисление, проверка всегда проходит успешно! В любом случае, этот ValidationAttribute никогда не может возвращать ошибку. - person Chris F Carroll; 24.11.2018