有没有办法在FluentValidation中指定默认验证规则?

时间:2018-05-02 00:50:50

标签: fluentvalidation

我们说我有以下课程:

class MyClass
{
    public string Foo { get; set; }
    public string Bar { get; set; }
    public string Baz { get; set; }
}

和验证规则如下:

RuleFor(c => c.Foo).NotEmpty();
RuleFor(c => c.Bar).NotNull();  // Bar is allowed to be empty string
RuleFor(c => c.Baz).NotEmpty();

有没有办法表达一个默认规则,用于验证没有指定特定规则的任何属性?这样的东西应该给出与上面相同的验证结果吗?

RuleFor(c => c.Bar).NotNull();
DefaultRule().NotEmpty();

1 个答案:

答案 0 :(得分:1)

所以,我向Jeremy Skinner(FluentValidation的创建者)提出了同样的问题,并很快得到了以下答案。

不,我不敢开箱即可。 FluentValidation背后的想法是提供一种方法来明确定义针对特定属性的强类型规则。这种方式违背了这个目的,所以我不喜欢包括这样的东西。 话虽这么说,如果你真的想要这个功能,你可以将它构建为扩展。您必须执行以下操作:

  1. 使用反射查找目标对象上的所有属性
  2. 检查该属性的验证器中是否已定义规则
  3. 对于没有规则的属性,构建PropertyRule实例
  4. 对于每个规则,请在默认
  5. 中应用所需的验证器

    这是一个简单的例子(未经测试)。您需要引入缓存以防止对验证器的每个实例化进行反映:

    public static class DefaultRuleExtension 
    {
        public static void DefaultRule<T>(this AbstractValidator<T> validator, Action<IRuleBuilder<T, object>> defaultRuleBuilder) 
        {
            var existingPropertiesWithRules = validator.OfType<PropertyRule>().Select(x => x.PropertyName).ToList();
    
            var properties = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(x => !existingPropertiesWithRules.Contains(x.Name));
    
            //TODO: Make sure you cache the properties and the expression tree so the reflection isn't performed on every instantiation.
            foreach (var property in properties) 
            {
                var expression = BuildGetterExpression<T>(property);
                var rule = PropertyRule.Create(expression);
    
                validator.AddRule(rule);
                var ruleBuilder = new RuleBuilder<T, object>(rule, validator);
                defaultRuleBuilder(ruleBuilder);
            }
        }
    
        private static Expression<Func<T, object>> BuildGetterExpression<T>(PropertyInfo prop) 
        {
            var param = Expression.Parameter(typeof(T), "x");
            Expression expression = Expression.PropertyOrField(param, prop.Name);
    
            if (prop.PropertyType.IsValueType)
                expression = Expression.Convert(expression, typeof(object));
    
            return Expression.Lambda<Func<T, object>>(expression, param);
        }
    }
    

    可以这样:

    public class DemoValidator : AbstractValidator<Person> {
        public DemoValidator() {
            this.DefaultRule(r => {
                r.NotEmpty();
            });
        }
    }