ASP.NET Custom ErrorMessage для поля Enum модели

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

[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>

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

форма вывода

3 ответа

Решение
  • В вашем оригинальном случае, [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

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

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

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

Определите свою модель:

      public enum Day
{
    None=0,
    Sunday = 1,
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday
}

а затем определите свой пользовательский атрибут проверки:

      sealed public class EnumCheck : ValidationAttribute
  {
    readonly Type t_;

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

   public override bool IsValid(object value)
    {

      if (((int)value)==0)
        {
            return false;

        }
      return Enum.IsDefined(t_, value);
    }
}

и, наконец, используйте это, на ваш взгляд:

      <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="0">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>
Другие вопросы по тегам