Conditional Nested Validation with FluentValidation

This example demonstrating how to conditionally validate nested objects using FluentValidation’s When method.

Scenario

Let’s say we have:

  • Person class with an Address property
  • We only want to validate the Address when HasAddress is true
Model Classes
public class Person
{
    public string Name { get; set; }
    public bool HasAddress { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public string ZipCode { get; set; }
}

Validator Implementation

public class PersonValidator : AbstractValidator<Person>
{
    public PersonValidator()
    {
        // Always validate name
        RuleFor(x => x.Name).NotEmpty().MaximumLength(100);
        
        // Only validate address when HasAddress is true
        When(x => x.HasAddress, () => 
        {
            RuleFor(x => x.Address)
                .NotNull()
                .WithMessage("Address must be provided when HasAddress is true");
                
            RuleFor(x => x.Address.Street)
                .NotEmpty()
                .When(x => x.Address != null)
                .WithMessage("Street is required");
                
            RuleFor(x => x.Address.City)
                .NotEmpty()
                .When(x => x.Address != null)
                .MaximumLength(50);
                
            RuleFor(x => x.Address.ZipCode)
                .NotEmpty()
                .When(x => x.Address != null)
                .Matches(@"^\d{5}(-\d{4})?$");
        });
    }
}

Alternative Approach (Using Child Validator)

public class PersonValidator : AbstractValidator<Person>
{
    public PersonValidator()
    {
        RuleFor(x => x.Name).NotEmpty().MaximumLength(100);
        
        When(x => x.HasAddress, () => 
        {
            RuleFor(x => x.Address)
                .NotNull()
                .SetValidator(new AddressValidator());
        });
    }
}

public class AddressValidator : AbstractValidator<Address>
{
    public AddressValidator()
    {
        RuleFor(x => x.Street).NotEmpty();
        RuleFor(x => x.City).NotEmpty().MaximumLength(50);
        RuleFor(x => x.ZipCode).NotEmpty().Matches(@"^\d{5}(-\d{4})?$");
    }
}

Usage Example

var person = new Person 
{
    Name = "Khan",
    HasAddress = false,
    Address = null
};

var validator = new PersonValidator();
var result = validator.Validate(person); // Won't validate address

Key Points:

  1. The When condition determines whether the nested rules should execute
  2. The nested rules are only evaluated if HasAddress is true
  3. We still need null checks for the address object itself
  4. The second approach using a separate validator is cleaner for complex nested objects

This pattern is useful when you want to validate complex objects only in certain scenarios, reducing unnecessary validation overhead.

FavoriteLoadingAdd to favorites
Spread the love

Author: Shahzad Khan

Software developer / Architect

Leave a Reply