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..