FluentValidation 规则链接不会在第一次失败时停止
FluentValidation rules chaining not stopping at first failure
我有一个模型:
public class DTO
{
public int[] StatementItems { get; set; }
}
我想验证一下:
StatementItems
不为空
StatementItems
不为空
StatementItems
不包含任何重复 ID
我创建的验证规则链是:
RuleFor(x => x.StatementItems).NotNull().NotEmpty().Must(x => x.Distinct().Count() == x.Count());
我有一个测试:
_validator.ShouldHaveValidationErrorFor(x => x.StatementItems, null as int[]);
当我 运行 测试传递空值时,我预计它会在链的第一条规则 (NotNull()
) 上失败并停在那里。但是,它抱怨 Must()
中使用的 lamda 值为空。
如果 NotNull()
失败,我认为 Must()
不应该是 运行 是不是错了?如果可以,这个规则应该怎么写?
谢谢
我在 FluentValidation
文档中没有看到它实际上保证了 short-circuiting。
如果您查看其来源:
public virtual ValidationResult Validate(ValidationContext<T> context)
{
...
var failures = nestedValidators.SelectMany(x => x.Validate(context));
return new ValidationResult(failures);
}
它将运行 通过*所有* 验证器(带有SelectMany()
)和returns 错误列表。
您唯一的选择似乎是强制检查您的 Must
规则。
.Must(x => x!= null && x.Distinct().Count() == x.Count())
//or, fluently:
.Must(x => x.Distinct().Count() == x.Count()).When(x => x! = null)
编辑:
我打算建议,由于 Validate()
是虚拟的,您可以在验证器中覆盖它以使其成为 short-circuit。但后来我意识到 nestedValidators
列表是私有的。所以是的,不..
尽管@NPras 的回答确实为我提供了解决方案,但我不喜欢复制 NotNull 规则这一事实。在对 FluentValidation 进行更多研究后,我使用 DependentRules
:
实现了它
RuleFor(x => x.StatementItems).NotNull().NotEmpty()
.DependentRules(d =>
d.RuleFor(x => x.StatementItems).Must(x => x.Distinct().Count() == x.Count())
);
所以现在 Must
条件仅在前两个规则有效时触发。
查看 FluentValidation 的 cascade mode。您可以像这样在第一次失败时使其 short-circuit:
this.RuleFor(x => x.StatementItems)
.Cascade(CascadeMode.Stop)
.NotNull()
.NotEmpty()
.Must(x => x.Distinct().Count() == x.Count());
此外,您可以在 AbstractValidator
子类的构造函数中配置它。那么你就不需要把它放在每条规则上了。
public MyInputValidator()
{
this.CascadeMode = CascadeMode.Stop;
}
我有一个模型:
public class DTO
{
public int[] StatementItems { get; set; }
}
我想验证一下:
StatementItems
不为空StatementItems
不为空StatementItems
不包含任何重复 ID
我创建的验证规则链是:
RuleFor(x => x.StatementItems).NotNull().NotEmpty().Must(x => x.Distinct().Count() == x.Count());
我有一个测试:
_validator.ShouldHaveValidationErrorFor(x => x.StatementItems, null as int[]);
当我 运行 测试传递空值时,我预计它会在链的第一条规则 (NotNull()
) 上失败并停在那里。但是,它抱怨 Must()
中使用的 lamda 值为空。
如果 NotNull()
失败,我认为 Must()
不应该是 运行 是不是错了?如果可以,这个规则应该怎么写?
谢谢
我在 FluentValidation
文档中没有看到它实际上保证了 short-circuiting。
如果您查看其来源:
public virtual ValidationResult Validate(ValidationContext<T> context)
{
...
var failures = nestedValidators.SelectMany(x => x.Validate(context));
return new ValidationResult(failures);
}
它将运行 通过*所有* 验证器(带有SelectMany()
)和returns 错误列表。
您唯一的选择似乎是强制检查您的 Must
规则。
.Must(x => x!= null && x.Distinct().Count() == x.Count())
//or, fluently:
.Must(x => x.Distinct().Count() == x.Count()).When(x => x! = null)
编辑:
我打算建议,由于 Validate()
是虚拟的,您可以在验证器中覆盖它以使其成为 short-circuit。但后来我意识到 nestedValidators
列表是私有的。所以是的,不..
尽管@NPras 的回答确实为我提供了解决方案,但我不喜欢复制 NotNull 规则这一事实。在对 FluentValidation 进行更多研究后,我使用 DependentRules
:
RuleFor(x => x.StatementItems).NotNull().NotEmpty()
.DependentRules(d =>
d.RuleFor(x => x.StatementItems).Must(x => x.Distinct().Count() == x.Count())
);
所以现在 Must
条件仅在前两个规则有效时触发。
查看 FluentValidation 的 cascade mode。您可以像这样在第一次失败时使其 short-circuit:
this.RuleFor(x => x.StatementItems)
.Cascade(CascadeMode.Stop)
.NotNull()
.NotEmpty()
.Must(x => x.Distinct().Count() == x.Count());
此外,您可以在 AbstractValidator
子类的构造函数中配置它。那么你就不需要把它放在每条规则上了。
public MyInputValidator()
{
this.CascadeMode = CascadeMode.Stop;
}