This example demonstrating how to conditionally validate nested objects using FluentValidation’s When
method.
Scenario
Let’s say we have:
- A
Person
class with anAddress
property - We only want to validate the
Address
whenHasAddress
istrue
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:
- The
When
condition determines whether the nested rules should execute - The nested rules are only evaluated if
HasAddress
is true - We still need null checks for the address object itself
- 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.