dalevross
dalevross

Reputation: 522

Using FluentValidation to validate mutually exclusive fields

I'm attempting to validate that only one of three fields has a value using FluentValidation.

RuleFor(x => x.Date1)
            .Must(x => !x.HasValue)
            .When(x => x.Date2.HasValue || x.Date3.HasValue)
            .WithMessage("Select only one of Date 1, Date 2 and Date 3");

This is repeated for the other 2 dates. As would be expected, this produces on message per rule that matches.

There are other rules involved, so is there a way to execute the other rules but fail on the first of these three? I've seen where I could set CascadeMode.StopOnFirstFailure globally but I want the other rules outside of these three to work as they currently do.

Upvotes: 8

Views: 3479

Answers (3)

Daniel Davis
Daniel Davis

Reputation: 622

Simple one rule for all 3 using xor. No extra properties needed. take out the unless if you want to force at least one to have a value.

 RuleFor(x => x).Cascade(CascadeMode.StopOnFirstFailure)
     .Must(x => (x.date1.HasValue ^ x.date2.HasValue) ^ x.date3.HasValue)
     .Unless(x => !x.date1.HasValue && !x.date2.HasValue && !x.date3.HasValue)
     .WithMessage("Select only one of Date 1, Date 2 and Date 3");

Upvotes: 5

Bryan
Bryan

Reputation: 5471

I would use something like

RuleFor(x => x).Must(ValidateDates).WithName("something");

<snip/>

private bool ValidateDates(SomeRequest request)
{
   //perform logic and return true/false;
}

Inside the ValidateDates method you have access to the full request.

For more examples of complex validation using Must take a look at this post - http://nodogmablog.bryanhogan.net/2015/04/complex-model-validation-using-fluent-validation/

Upvotes: 3

dalevross
dalevross

Reputation: 522

I decide to go down another route. It feels elegant but I'll know if it passes the code review.

I created a new property

    public IEnumerable<DateTime?> MutuallyExclusiveDates
    {
        get
        {
            return new List<DateTime?>()
            {
                Date1,
                Date2,
                Date3
            };

        }
    }

Then I added this rule

 RuleFor(x => x.MutuallyExclusiveDates)
            .Must(x => x.Count(d => d.HasValue) <= 1)
            .WithMessage("Select only one of Date 1, Date 2 and Date 3");

Upvotes: 6

Related Questions