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 } }
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; }); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With