Renato Pereira
Renato Pereira

Reputation: 864

Fluent Validation chain rule not working with multiple When conditions

I got a really interesting behavior. I have the two test cases below:

[Fact]
public void Ctor_WhenNeverIsTrueAndAfterOcurrenceIsNotNull_HasError()
{
    // arrange
    var reccurenceEnd = new RecurrenceEnd()
    {
         IsNever = true,
         AfterOccurence = 1
     };

     // act
     var validator = GetValidator();

     // assert
     validator.ShouldHaveValidationErrorFor(p => p.AfterOccurence, reccurenceEnd);
}

[Fact]
public void Ctor_WhenNeverIsFalseAndAfterOccurenceIsNullAndByDateIsNull_HasError()
{
    // arrange
    var reccurenceEnd = new RecurrenceEnd()
    {
        IsNever = false,
        AfterOccurence = null,
        ByDate = null
    };

    // act
    var validator = GetValidator();

    // assert
    validator.ShouldHaveValidationErrorFor(p => p.AfterOccurence, reccurenceEnd);
}

On my validator, if I have the following, the first test fails and the second passes. If I change the order of the rules, the first test passes and the second fails.

RuleFor(dto => dto.AfterOccurence)
    .Cascade(CascadeMode.StopOnFirstFailure)
    .Null()
        .When(p => p.IsNever == true)
    .NotEmpty()
        .When(p => p.IsNever == false && p.ByDate == null);

If I change my validator to the following, both tests pass.

RuleFor(dto => dto.AfterOccurence)
    .Null()
        .When(p => p.IsNever);
RuleFor(dto => dto.AfterOccurence)
    .NotEmpty()
        .When(p => p.IsNever == false && p.ByDate == null);

Am I setting up wrong?

Upvotes: 10

Views: 13278

Answers (1)

Renato Pereira
Renato Pereira

Reputation: 864

Ok, got an answer from FluentValidation's github-issues:

Hi, this is related to you use of the When condition. By default, a When condition applies to all previous validators in the same RuleFor call, so when you use a single call to RuleFor, you’re essentially doubling up on conditions. You should either use separate calls to RuleFor (as you have in your second example), or use the other overload of When that allows you to specify ApplyConditionTo.CurrentValidator

The second solution would be:

RuleFor(dto => dto.AfterOccurence)
    .Cascade(CascadeMode.StopOnFirstFailure)
    .Null()
        .When(p => p.IsNever == true)
    .NotEmpty()
        .When(p => p.IsNever == false && p.ByDate == null, ApplyConditionTo.CurrentValidator);

Upvotes: 17

Related Questions