Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Child Model Validation using Parent Model Values. Fluent Validation. MVC4

Below is a simplified version of my problem.

I can not flatten the model. There is a List of "children" that I need to validate a birthday.

I can not seem to reference the date in the Parent class and was wondering how this is done in Fluent Validation?

Model

[Validator(typeof(ParentValidator))] public class Parent {     public string Name { get; set; }     public DateTime Birthdate { get; set; }      public List<Child> Children { get; set; } }  public class Child {     public string ChildProperty{ get; set; }     public DateTime Birthdate { get; set; } } 

Validator

public class ParentValidator : AbstractValidator<Parent> {     public ParentValidator()     {          RuleFor(model => model.Name).NotEmpty();          RuleForEach(model => model.Children).SetValidator(new ChildValidator());     } }  public class ChildValidator : AbstractValidator<Child> {     public ChildValidator()     {         RuleFor(model => model.ChildProperty).NotEmpty();         //Compare birthday to make sure date is < Parents birthday     } } 
like image 399
Dustin Harrell Avatar asked Sep 10 '13 13:09

Dustin Harrell


1 Answers

Create a custom property validator like this

public class AllChildBirtdaysMustBeLaterThanParent : PropertyValidator {     public AllChildBirtdaysMustBeLaterThanParent()         : base("Property {PropertyName} contains children born before their parent!")     {     }      protected override bool IsValid(PropertyValidatorContext context)     {         var parent = context.ParentContext.InstanceToValidate as Parent;         var list = context.PropertyValue as IList<Child>;          if (list != null)         {             return ! (list.Any(c => parent.BirthDay > c.BirthDay));         }          return true;     } } 

Add rules like this

public class ParentValidator : AbstractValidator<Parent> {     public ParentValidator()     {         RuleFor(model => model.Name).NotEmpty();         RuleFor(model => model.Children)                .SetValidator(new AllChildBirtdaysMustBeLaterThanParent());          // Collection validator         RuleFor(model => model.Children).SetCollectionValidator(new ChildValidator());     } } 

Alternative to the Custom Property validator is to use the Custom method:

    public ParentValidator()     {         RuleFor(model => model.Name).NotEmpty();         RuleFor(model => model.Children).SetCollectionValidator(new ChildValidator());          Custom(parent =>         {             if (parent.Children == null)                 return null;              return parent.Children.Any(c => parent.BirthDay > c.BirthDay)                ? new ValidationFailure("Children", "Child cannot be older than parent.")                : null;         });     } 

Crude way of showing indicies that failed: (should probably be name of some other identifier)

public class ParentValidator : AbstractValidator<Parent> {     public ParentValidator()     {         RuleFor(m => m.Children).SetCollectionValidator(new ChildValidator());          Custom(parent =>         {             if (parent.Children == null)                 return null;              var failIdx = parent.Children.Where(c => parent.BirthDay > c.BirthDay).Select(c => parent.Children.IndexOf(c));             var failList = string.Join(",", failIdx);              return failIdx.Count() > 0                ? new ValidationFailure("Children", "Child cannot be older than parent. Fail on indicies " + failList)                : null;         });     }  } 
like image 103
Tommy Grovnes Avatar answered Sep 23 '22 01:09

Tommy Grovnes