Проверка модели представления после привязки пользовательской модели
У меня есть модель представления, которая реализует IValidatableObject
, которая содержит строку и коллекцию другой модели представления, примерно так:
public sealed class MainViewModel
{
public string Name { get; set; }
public ICollection<OtherViewModel> Others { get; set; }
}
Моя проверка проверяет каждый объект в Others
на разные правила, используя контракт, предоставленный IValidatableObject
:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
foreach (var other in this.Others)
{
// validate or yield return new ValidationResult
}
}
Из-за сложной структуры реального MainViewModel
мне пришлось создать настраиваемое связующее устройство, которое перестраивает модель и присваивает данные POST соответствующим компонентам. Проблема, которую я получаю, заключается в том, что ничто не проверяется, что приводит к ошибкам проверки на уровне контекста, поскольку это нарушает определенные ограничения базы данных, и я не уверен, что я делаю неправильно. Я предположил, что ModelState.IsValid
будет ссылаться на Validate
на моей модели просмотра, но, похоже, это не так.
Мое связующее устройство выглядит следующим образом:
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
int modelId = (int)controllerContext.RouteData.Values["id"];
// query the database and re-build the components of the view model
// iterate the POST data and assign to the model where necessary
// should I be calling something here to validate the model before it passed to the controller?
return model;
}
Любая помощь оценивается!
Validator.TryValidateObject
Хорошо, кажется, я немного ближе. Теперь я могу запустить мой метод IValidatableObject
, добавив следующее в мое настраиваемое связующее устройство:
var validationResults = new HashSet<ValidationResult>();
var isValid = Validator.TryValidateObject(model, new ValidationContext(model, null, null), validationResults, true);
Кажется, что Validator.TryValidateObject
вызывает метод проверки и установка последнего параметра true
заставляет его проверять все свойства. Тем не менее, я теперь застрял в получении validationResults
к контроллеру, чтобы их можно было использовать значимым образом.
Ответы
Ответ 1
Я должен был понять, что я мог бы использовать ModelState.AddModelError
через настраиваемое связующее, мне удалось заставить это работать правильно, добавив следующее в мое настраиваемое связующее устройство перед возвратом модели контроллеру:
var validationResults = new HashSet<ValidationResult>();
var isValid = Validator.TryValidateObject(model, new ValidationContext(model, null, null), validationResults, true);
if (!isValid)
{
foreach (var result in validationResults)
{
bindingContext.ModelState.AddModelError("", result.ErrorMessage);
}
}
return model;
Теперь это возвращает список всех ошибок на моей странице, а проверка ModelState.IsValid
моего действия с контроллером теперь возвращается false
.
Ответ 2
Пол отличный ответ может быть реорганизован в общий метод validate-and-convert в ModelState
следующим образом (например, в помощнике или базе CustomModelBinder
). Кроме того, привязки к проверенным свойствам сохраняются.
public static void DoValidation(ModelBindingContext bindingContext,
IValidatableObject model)
{
var validationResults = new HashSet<ValidationResult>();
var isValid = Validator.TryValidateObject(model,
new ValidationContext(model, null, null), validationResults, true);
if (!isValid)
{
var resultsGroupedByMembers = validationResults
.SelectMany(_ => _.MemberNames.Select(
x => new {MemberName = x ?? "",
Error = _.ErrorMessage}))
.GroupBy(_ => _.MemberName);
foreach (var member in resultsGroupedByMembers)
{
bindingContext.ModelState.AddModelError(
member.Key,
string.Join(". ", member.Select(_ => _.Error)));
}
}
}