自定义模型绑定后验证视图模型

时间:2012-12-03 13:33:35

标签: asp.net-mvc-3 validation custom-model-binder

我有一个实现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 个答案:

答案 0 :(得分:10)

我应该已经意识到我可以通过自定义绑定器使用ModelState.AddModelError,我已经设法通过在将模型返回到控制器之前将以下内容添加到我的自定义模型绑定器中来使其正常工作: / p>

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

答案 1 :(得分:2)

保罗的优秀答案可以重构为通用验证并转换为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)));
        }
    }
}