Проверка модели представления после привязки пользовательской модели

У меня есть модель представления, которая реализует 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's 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 к контроллеру, чтобы они могли быть использованы осмысленно.

2 ответа

Решение

Я должен был понять, что я мог бы использовать 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,

Великий ответ Павла может быть преобразован в общий метод проверки и преобразования в 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)));
        }
    }
}
Другие вопросы по тегам