Соображения после обмена комментариями:
Согласное и ожидаемое поведение разработчиков заключается в том, что метод IValidatableObject
*1007* вызывается только в том случае, если атрибуты проверки не запущены. Короче говоря, ожидаемый алгоритм таков (взят из предыдущей ссылки):
- Проверка атрибутов уровня свойства
- Если какие-либо валидаторы недействительны, прервать валидацию, возвращая сбой (ы)
- Проверка атрибутов уровня объекта
- Если какие-либо валидаторы недействительны, прервать валидацию, возвращая сбой (ы)
- Если на десктопной платформе и объект реализует IValidatableObject, тогда вызовите его метод Validate и верните любые ошибки
Однако, с использованием кода вопроса, Validate
вызывается даже после [Required]
срабатывания . Это кажется очевидной ошибкой MVC . Который сообщается здесь .
Три возможных обходных пути:
Существует обходной путь здесь , хотя с некоторыми заявленными проблемами с его использованием, кроме нарушения ожидаемого поведения MVC. С некоторыми изменениями, чтобы избежать появления более одной ошибки для одного и того же поля, приведем код:
viewModel
.Validate(new ValidationContext(viewModel, null, null))
.ToList()
.ForEach(e => e.MemberNames.ToList().ForEach(m =>
{
if (ModelState[m].Errors.Count == 0)
ModelState.AddModelError(m, e.ErrorMessage);
}));
Забудьте IValidatableObject
и используйте только атрибуты. Он чистый, прямой, лучше справляется с локализацией и, что лучше всего, его можно использовать повторно среди всех моделей. Просто внедрите ValidationAttribute для каждой проверки, которую вы хотите выполнить. Вы можете проверить все модели или конкретные свойства, это зависит от вас. Помимо атрибутов, доступных по умолчанию (DataType, Regex, Required и все такое), есть несколько библиотек с наиболее часто используемыми проверками. Тот, который реализует «отсутствующие» , является FluentValidation .
- Реализация только
IValidatableObject
интерфейса отбрасывания аннотаций данных . Это кажется разумным вариантом, если это очень специфическая модель, и она не требует большой проверки. В большинстве случаев разработчик будет выполнять все эти регулярные и обычные проверки (т. Е. Обязательные и т. Д.), Что приводит к дублированию кода в проверках, уже реализованных по умолчанию, если использовались атрибуты. Также нет возможности повторного использования.
<Ч />
Ответ перед комментариями:
Прежде всего, я создал новый проект с нуля только с предоставленным вами кодом. НИКОГДА не вызывал одновременно аннотации данных и метод проверки.
В любом случае, знайте это,
По замыслу, MVC3 добавляет атрибут [Required]
к необнуляемым типам значений, таким как int
, DateTime
или, да, decimal
. Таким образом, даже если вы удалите обязательный атрибут из этого decimal
, он будет работать так же, как и тот, который там есть.
Это спорно для его неправильности (или нет), но, как она разработана.
В вашем примере:
- DataAnnotation 'срабатывает, если присутствует [Обязательный] и значение не указано. Полностью понятно с моей точки зрения
- DataAnnotation 'срабатывает, если нет [Обязательный], но значение не обнуляется. Спорно, но я склонен согласиться с ним, потому что, если свойство не обнуляемым, значение должно быть введено, в противном случае не показать его пользователю или просто использовать обнуляемым
decimal
.
Такое поведение, как кажется, может быть отключено с помощью вашего метода Application_Start:
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
Полагаю, имя свойства не требует пояснений.
В любом случае, я не понимаю, почему вы хотите, чтобы пользователь вводил что-то ненужное и не делал это свойство обнуляемым. Если это null , тогда ваша задача проверить его, , если вы не хотите, чтобы он был нулевым , до проверки в контроллере.
public ActionResult Index(HomeViewModel viewModel)
{
// Complete values that the user may have
// not filled (all not-required / nullables)
if (viewModel.Decimal == null)
{
viewModel.Decimal = 0m;
}
// Now I can validate the model
if (ModelState.IsValid)
{
HomeModel.ProcessHome(viewModel);
return RedirectToAction("Ok");
}
}
Как вы думаете, что это неправильно в этом подходе или не должно быть так?