Validating a view model after custom model binding

只愿长相守 提交于 2019-11-30 19:04:24

问题


I have a view model that implements IValidatableObject that contains a string and a collection of another view model, something like this:

public sealed class MainViewModel
{
    public string Name { get; set; }
    public ICollection<OtherViewModel> Others { get; set; }
}

My validation checks each object in Others against different rules using the contract provided by IValidatableObject:

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
    foreach (var other in this.Others)
    {
        // validate or yield return new ValidationResult
    }
}

Because of the complex structure of the real MainViewModel I have had to create a custom model binder which re-builds the model and assigns POST data to the relevant components. The problem that I'm getting is that nothing is getting validated resulting in validation errors at the context level as it violates certain database constraints and I'm not sure what I'm doing wrong - I assumed that ModelState.IsValid would invoke the Validate method on my view model but it doesn't seem to go down that way.

My model binder looks like this:

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;
}

Any help appreciated!

Validator.TryValidateObject

OK, seems I'm a little closer. I can now get my IValidatableObject method to run by adding the following to my custom model binder:

var validationResults = new HashSet<ValidationResult>();
var isValid = Validator.TryValidateObject(model, new ValidationContext(model, null, null), validationResults, true);

Seems that Validator.TryValidateObject invokes the validation method and setting the last parameter to true causes it to validate all properties. However, I'm now stuck with getting the validationResults to the controller so they can be used in a meaningful way.


回答1:


I should have realised that I could use the ModelState.AddModelError through a custom binder, I've managed to get this working correctly now by adding the following to my custom model binder before returning the model to the controller:

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;

This now returns a list of all errors to my page and the ModelState.IsValid check on my controller action is now returning false.




回答2:


Paul's great answer can be refactored into a generic validate-and-convert to ModelState method as follows (e.g. in a helper or CustomModelBinder base). In addition, the bindings to the validated properties are retained.

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)));
        }
    }
}


来源:https://stackoverflow.com/questions/13684354/validating-a-view-model-after-custom-model-binding

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!