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

前端 未结 4 724
我寻月下人不归
我寻月下人不归 2020-12-25 11:13

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

相关标签:
4条回答
  • 2020-12-25 11:36

    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;
            });
        }
    
    }
    
    0 讨论(0)
  • Nowadays the answer by @johnny-5 can be simplified even further by using the SetCollectionValidator extension method and passing the parent object to the child validator:

    public class ParentValidator : AbstractValidator<Parent>
    {
        public ParentValidator()
        {
             RuleFor(model => model.Name).NotEmpty();
             RuleFor(model => model.Children)
                 .SetCollectionValidator(model => new ChildValidator(model))
        }
    }
    
    public class ChildValidator : AbstractValidator<Child>
    {
        public ChildValidator(Parent parent)
        {
            RuleFor(model => model.ChildProperty).NotEmpty();
            RuleFor(model => model.Birthday).Must(birthday => parent.Birthday < birthday);
        }
    }
    
    0 讨论(0)
  • 2020-12-25 11:44

    Building on the answer of @kristoffer-jalen it is now:

    public class ParentValidator : AbstractValidator<Parent>
    {
        public ParentValidator()
        {
             RuleFor(model => model.Name).NotEmpty();
             //RuleFor(model => model.Children)
             //    .SetCollectionValidator(model => new ChildValidator(model))
             RuleForEach(model => model.Children)
                    .SetValidator(model => new ChildValidator(model));
        }
    }
    
    public class ChildValidator : AbstractValidator<Child>
    {
        public ChildValidator(Parent parent)
        {
            RuleFor(model => model.ChildProperty).NotEmpty();
            RuleFor(model => model.Birthday).Must(birthday => parent.Birthday < birthday);
        }
    }
    

    as SetCollectionValidator is deprecated.

    0 讨论(0)
  • 2020-12-25 11:58

    There is a way easier way to do this using set child validator:

    public class ChildValidator : AbstractValidator<Child>
    {
        public ChildValidator(Parent parent)
        {
            RuleFor(model => model.ChildProperty).NotEmpty();
            RuleFor(model => model.Birthday).Must(birthday => parent.Birthday > birthday);
    
        }
    }
    
    public class ParentValidator : AbstractValidator<Parent>
    {
        public ParentValidator()
        {
             RuleFor(model => model.Name).NotEmpty();
        }
    
        public override ValidationResult Validate(Parent parent)
        {
            RuleFor(model => model.Children).SetCollectionValidator(new ChildValidator(this));
    
            return base.Validate();
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题