Model Validation not working with all properties

2019-09-11 05:42发布

问题:

I have the following ViewModel:

 public class MyViewModel:IValidatableObject
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public DateTime? Birthday { get; set; }

        public int Status { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            if (string.IsNullOrWhiteSpace(Name))
                yield return new ValidationResult("Please fill the name", new string[] { "Name" });

            if (Birthday.HasValue == false)
                yield return new ValidationResult("Please fill the birthday", new string[] { "Birthday" });

            if(Status <= 0)
                yield return new ValidationResult("Please fill the status", new string[] { "Status" });
        }
    }

Controller:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Name,Birthday,Status")] MyViewModel myViewModel)
{
    if (ModelState.IsValid)
    {
        db.MyViewModels.Add(myViewModel);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(myViewModel);
}

I would like to display all the validation messages at the same time, however it shows first status and then the other two properties.

回答1:

This is due to the order in which validation happens. First the ModelBinder does it's job, and if that passes, since you've created a self validating viewmodel by implementing IValidatableObject, the Validate method is called. In the first screenshot the modelbinding process is failing so Validate is never called. In the second screenshot, modelbinding succeeds, but Validate() fails.

You can solve this by using DataAnnotations instead of implementing IValidatableObject like so:

    public class MyViewModel:IValidatableObject
    {
        public int Id { get; set; }
        [Required]
        public string Name { get; set; }
        [Required]
        public DateTime Birthday { get; set; }
        [Required, Range(0, Int32.MaxValue)]
        public int Status { get; set; }
    }