1

I've got a class that has [Required] decorations on some attributes, but also needs some custom validation, so it uses IValidatableObject.

Class

public class ModelCourse : IValidatableObject
{
    //some other code...

    [DisplayName("Course Name")]
    [Required]
    [StringLength(100, MinimumLength=1)]
    public String name { get; set; }

    //more code...

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (courseGradeLevels == null || courseGradeLevels.Count < 1)
        {
            yield return new ValidationResult("You must select at least one grade level.");
        }
        if ((courseLength == CourseLength.Other) && string.IsNullOrEmpty(courseLengthDescription))
        {
            yield return new ValidationResult("Course Length Description is Required if Length is Other.");
        }
    }
}

Validation

        //course is one of a few child objects in this class
        bool dbDetailsValid = TryValidateModel(dbDetails);

        ViewData["courseValid"] = !ModelState.ContainsKey("course");

        foreach(KeyValuePair<string, ModelState> pair in ModelState.Where(x => x.Value.Errors.Count > 0))
        {
            ViewData[pair.Key + "ErrorList"] = pair.Value.Errors.ToList();
        }

When I run this code, a blank string for name doesn't result in an error in ModelState. The custom validation logic works as expected, but I don't get why TryValidateModel isn't picking up on the decorations... is my only option to manually check each required field?

pennstatephil
  • 1,593
  • 3
  • 22
  • 43
  • your current code - StringLength expects a max of 100. you also need to specify the min which would be 1 in this case. (named parameter: MinimumLength) – Ahmed ilyas Feb 06 '14 at 19:12
  • good idea, but I just tried adding the attribute and still no error in the ModelState – pennstatephil Feb 06 '14 at 19:16
  • well the minimumlength is still required to be added here. in any event, what is the value for dbDetailsValid when you execute the TryValidateModel? – Ahmed ilyas Feb 06 '14 at 19:18
  • it's false, but it's failing on a different child object. `ModelState.ContainsKey("course")` is returning false. No errors on the course. If I violate one of the policies in my `Validate` method, it shows up as expected. – pennstatephil Feb 06 '14 at 19:20
  • More info: tried commenting out all the custom validation, leaving just the `[Required]` and `[StringLength...]` attributes. Still no error when calling validate on the parent, with a confirmed empty string in the child. – pennstatephil Feb 10 '14 at 23:28

1 Answers1

3

Thanks to @nick nieslanik to pointing me to Understanding ValidationContext in DataAnnotations, I have my answer.

Inside my custom validation method, I use reflection to loop through each property, and call TryValidateProperty on each one, adding it to the results if it doesn't validate:

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        List<ValidationResult> results = new List<ValidationResult>();
        foreach (PropertyInfo property in this.GetType().GetProperties())
        {
            Validator.TryValidateProperty(property.GetValue(this), new ValidationContext(this, null, null) { MemberName = property.Name }, results);
            if (results.Count > 0)
            {
                foreach (ValidationResult err in results)
                {
                    yield return new ValidationResult(err.ErrorMessage);
                }
                results.Clear();
            }
        }
        //the rest of the validation happens here...
    }
Community
  • 1
  • 1
pennstatephil
  • 1,593
  • 3
  • 22
  • 43