【问题标题】:FluentValidation multiple validatorsFluentValidation 多个验证器
【发布时间】:2012-10-23 07:16:50
【问题描述】:

我可以向一个对象添加多个验证器吗?例如:

public interface IFoo
{
    int Id { get; set; }
    string Name { get; set; }
}

public interface IBar
{
    string Stuff { get; set; }
}

public class FooValidator : AbstractValidator<IFoo>
{
    public FooValidator ()
    {
        RuleFor(x => x.Id).NotEmpty().GreaterThan(0);
    }
}

public class BarValidator : AbstractValidator<IBar>
{
    public BarValidator()
    {
        RuleFor(x => x.Stuff).Length(5, 30);
    }
}

public class FooBar : IFoo, IBar
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Stuff { get; set; }
}

public class FooBarValidator : AbstractValidator<FooBar>
{
    public FooBarValidator()
    {
        RuleFor(x => x)
            .SetValidator(new FooValidator())
            .SetValidator(new BarValidator());
    }
}

运行测试。

FooBarValidator validator = new FooBarValidator();
validator.ShouldHaveValidationErrorFor(x => x.Id, 0);

我收到了InvalidOperationException:

无法为表达式 x => x 自动确定属性名称。请通过调用“WithName”指定自定义属性名称。

有什么方法可以实现这一点,还是我试图以不适合使用的方式使用 FluentValidation?

【问题讨论】:

    标签: c# .net fluentvalidation


    【解决方案1】:

    RuleFor 正在尝试创建属性级规则。您还可以使用 AddRule 函数添加通用规则。

    使用它,我创建了一个复合规则概念证明。它接收一组其他验证器并运行它们。 yield break 代码直接来自 FluentValidator 的 DelegateValidator。我不确定如何处理它,所以我从源头上抓住了它。我没有追踪它的全部目的,但一切似乎都按原样工作:)

    代码

    public interface IFoo
    {
        int Id { get; set; }
        string Name { get; set; }
    }
    
    public interface IBar
    {
        string Stuff { get; set; }
    }
    
    public class FooValidator : AbstractValidator<IFoo>
    {
        public FooValidator()
        {
            RuleFor(x => x.Id).NotEmpty().GreaterThan(0);
        }
    }
    
    public class BarValidator : AbstractValidator<IBar>
    {
        public BarValidator()
        {
            RuleFor(x => x.Stuff).Length(5, 30);
        }
    }
    
    public class FooBar : IFoo, IBar
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Stuff { get; set; }
    }
    
    public class CompositeValidatorRule : IValidationRule
    {
        private IValidator[] _validators;
    
        public CompositeValidatorRule(params IValidator[] validators)
        {
            _validators = validators;
        }
    
        #region IValidationRule Members
        public string RuleSet
        {
            get; set;
        }
    
        public IEnumerable<ServiceStack.FluentValidation.Results.ValidationFailure> Validate(ValidationContext context)
        {
            var ret = new List<ServiceStack.FluentValidation.Results.ValidationFailure>();
    
            foreach(var v in _validators)
            {
                ret.AddRange(v.Validate(context).Errors);
            }
    
            return ret;
        }
    
        public IEnumerable<ServiceStack.FluentValidation.Validators.IPropertyValidator> Validators
        {
            get { yield break; }
        }
        #endregion
    }
    
    public class FooBarValidator : AbstractValidator<FooBar>
    {
        public FooBarValidator()
        {
            AddRule(new CompositeValidatorRule(new FooValidator(), new BarValidator()));
        }
    }
    

    基础测试用例:

        [TestMethod]
        public void TestValidator()
        {
            FooBarValidator validator = new FooBarValidator();
            var result = validator.Validate(new FooBar());
    
        }
    

    我希望这会有所帮助。

    【讨论】:

    • 通过一点反思,您可以自动找到复合验证器应该连接的所有验证器(即找到所有接口和基类链,看看是否有 IValidator他们,如果是这样,自动将其添加到列表中)。只是取决于你想走多远。我可能会选择使用反射来查找所有验证器而不是手动列表。
    • 这个回复,虽然它似乎仍然有效(稍作修改),但已经很老了。有没有内置的方法可以做到这一点?我能找到的最接近的是已添加的“包含”方法,但它只允许包含与执行包含的方法相同类型的 IValidators,因此不能按照上面的示例用于多个接口。
    【解决方案2】:

    另一种可能性是覆盖验证:

    public override ValidationResult Validate(ValidationContext<FooBar> context)
    {
        var fooResult = new FooValidator().Validate(context.InstanceToValidate);
        var barResult = new BarValidator().Validate(context.InstanceToValidate);
    
        var errors = new List<ValidationFailure>();
        errors.AddRange(fooResult.Errors);
        errors.AddRange(barResult.Errors);
    
        return new ValidationResult(errors);
    }
    

    【讨论】:

      【解决方案3】:

      “包含规则。您可以包含来自其他验证器的规则,只要它们验证相同的类型。”

      public class PersonValidator : AbstractValidator<Person> {
          public PersonValidator() {
              Include(new PersonAgeValidator());
              Include(new PersonNameValidator());
          }
      }
      

      https://docs.fluentvalidation.net/en/latest/including-rules.html

      【讨论】:

        【解决方案4】:

        如果这有助于您尝试做的事情,您可以使用 RuleSet 应用不同类型的验证:

        FluentValidation RuleSets

        【讨论】:

        • RuleSet 不适合。我总是想运行所有的验证规则。我正在尝试构建具有许多相同属性的 DTO,因此在 DTO 中没有嵌套 obj 属性的情况下重用验证器会很好。
        猜你喜欢
        • 2015-10-29
        • 2011-12-03
        • 1970-01-01
        • 1970-01-01
        • 2019-09-17
        • 2020-01-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多