36

以下是我的问题的简化版本。

我无法展平模型。我需要一个“孩子”列表来验证生日。

我似乎无法在 Parent 类中引用日期,并且想知道在 Fluent Validation 中这是如何完成的?

模型

[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; }
}

验证器

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
    }
}
4

4 回答 4

29

像这样创建一个自定义属性验证器

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;
    }
}

添加这样的规则

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());
    }
}

自定义属性验证器的替代方法是使用自定义方法:

    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;
        });
    }

显示失败指标的粗略方式:(可能应该是其他标识符的名称)

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;
        });
    }

}
于 2013-09-10T13:35:27.330 回答
22

编辑: SetCollectionValidator 已被弃用,但现在可以使用 RuleForEach 完成相同的操作:

public class ParentValidator : AbstractValidator<Parent>
{
    public ParentValidator()
    {
         this.RuleFor(model => model.Name).NotEmpty();
         this.RuleForEach(model => model.Children)
                .SetValidator(model => new ChildValidator(model));
    }
}

public class ChildValidator : AbstractValidator<Child>
{
    public ChildValidator(Parent parent)
    {
        this.RuleFor(model => model.ChildProperty).NotEmpty();
        this.RuleFor(model => model.Birthday).Must(birthday => parent.Birthday < birthday);
    }
}
于 2016-02-03T20:40:48.473 回答
11

如今,@johnny-5 的答案SetCollectionValidator可以通过使用扩展方法并将父对象传递给子验证器来进一步简化:

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);
    }
}
于 2017-11-16T10:09:47.270 回答
8

基于@kristoffer-jalen 的答案,现在是:

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);
    }
}

SetCollectionValidator弃用。

于 2019-10-31T12:22:12.833 回答