FluentValidation rules chaining not stopping at first failure
Check out FluentValidation's cascade mode. You can make it short-circuit on the first failure like this:
this.RuleFor(x => x.StatementItems)
.Cascade(CascadeMode.Stop)
.NotNull()
.NotEmpty()
.Must(x => x.Distinct().Count() == x.Count());
Also, you can configure this in your AbstractValidator
subclass's constructor. Then you won't need to put it on every rule.
public MyInputValidator()
{
this.CascadeMode = CascadeMode.Stop;
}
Although @NPras's answer did supply my with a solution, I didn't like the fact that I'm duplicating the NotNull rule. After a bit more research on FluentValidation I have implemented it using DependentRules
:
RuleFor(x => x.StatementItems).NotNull().NotEmpty()
.DependentRules(d =>
d.RuleFor(x => x.StatementItems).Must(x => x.Distinct().Count() == x.Count())
);
So now the Must
condition is only fired when the previous two rules are valid.
I don't see in the FluentValidation
documentation that it actually guarantees short-circuiting.
If you look in its source:
public virtual ValidationResult Validate(ValidationContext<T> context)
{
...
var failures = nestedValidators.SelectMany(x => x.Validate(context));
return new ValidationResult(failures);
}
It will run through *all* the validators (with the SelectMany()
) and returns a list of errors.
Your only option seems to be to force a check on your Must
rule.
.Must(x => x!= null && x.Distinct().Count() == x.Count())
//or, fluently:
.Must(x => x.Distinct().Count() == x.Count()).When(x => x! = null)
EDIT:
I was going to suggest that since Validate()
is virtual, you could just override it in your validator to make it short-circuit. But then I realised that the nestedValidators
list is private. So yeah, no..