在 FluentValidation 的 CustomValidator 中访问 Parent DisplayName

Access Parent DisplayName in CustomValidator in FluentValidation

我有一个 ViewModel (BarViewModel),它有几个类型为 Person 的属性(OwnerCustomer)。我想要一个自定义验证器 (PersonValidator) 来验证这些属性。我确实想访问自定义验证器中那些 Person 属性的 DisplayAttribute。但是,如果我使用自定义验证器,PropertyName 的结果为 null,但如果我直接在 BarViewModelValidator 中使用验证,则不会。如何在我的自定义验证器中访问 DisplayAttribute

errors 变量的结果:

" 必须年满 18 岁。(Child 验证者)"

“客户必须年满 18 岁。(Parent 验证者)”

[TestFixture]
public class BarViewModelTests
{
    public class BarViewModel
    {
        [Display(Name = "Owner of bar")]
        public Person Owner { get; set; } = new Person();

        [Display(Name = "A Customer")]
        public Person Customer { get; set; } = new Person();
    }

    public class Person
    {
        public int Age { get; set; }
    }

    public class PersonValidator : AbstractValidator<Person>
    {
        public PersonValidator()
        {
            RuleFor(p => p).Custom((_, context) =>
            {
                if ((context.InstanceToValidate as Person)!.Age < 18)
                {
                    context.AddFailure($"{context.PropertyName}.{nameof(Person.Age)}", $"{context.DisplayName} must be over 18 years. (Child Validator)");
                }
            });
        }
    }

    public class BarViewModelValidator : AbstractValidator<BarViewModel>
    {
        public BarViewModelValidator()
        {
            RuleFor(p => p.Owner).SetValidator(new PersonValidator());
            RuleFor(p => p.Customer).SetValidator(new PersonValidator());

            RuleFor(p => p.Customer).Custom((_, context) =>
            {
                if ((context.InstanceToValidate as BarViewModel)!.Customer.Age < 18)
                {
                    context.AddFailure($"{context.PropertyName}.{nameof(Person.Age)}", $"{context.DisplayName} must be over 18 years. (Parent Validator)");
                }
            });
        }
    }

    private BarViewModelValidator _validator;

    [SetUp]
    public void Setup()
    {
        _validator = new BarViewModelValidator();
    }

    [Test]
    public void Should_have_proper_display_name()
    {
        var model = new BarViewModel
        {
            Owner = new Person
            {
                Age = 20
            },
            Customer = new Person
            {
                Age = 15
            }
        };

        var errors = _validator.TestValidate(model).Errors;
    }
}

我知道这行得通,但这不是我想要解决问题的方式:

public class PersonValidator : AbstractValidator<Person>
    {
        public PersonValidator(string displayName)
        {
            RuleFor(p => p).Custom((_, context) =>
            {
                if ((context.InstanceToValidate as Person)!.Age < 18)
                {
                    context.AddFailure($"{context.PropertyName}.{nameof(Person.Age)}", $"{displayName} must be over 18 years. (Child Validator)");
                }
            });
        }
    }

    public class BarViewModelValidator : AbstractValidator<BarViewModel>
    {
        public BarViewModelValidator()
        {
            RuleFor(p => p.Owner).SetValidator(new PersonValidator("Owner of bar"));
            RuleFor(p => p.Customer).SetValidator(new PersonValidator("A Customer"));
        }
    }

子验证器不知道父验证器,因此他们无法像这样访问父 属性 名称。所以剩下三个选项:

  • 通过构造函数显式传递它并使用SetValidator()
  • 直接在父验证器中定义子规则
  • 改为定义扩展并像子验证器一样使用它