Fluent Validation
Conditional Validation
Data Validation
.NET Validation
Validation Framework

Conditional Validation using Fluent Validation

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Conditional validation in FluentValidation lets you run rules only when a business condition is true. This is useful when required fields depend on a payment method, workflow step, feature flag, or other runtime context instead of being universally required.

Use When, Unless, and Otherwise

The most common conditional API is When, which scopes rules behind a predicate:

csharp
1using FluentValidation;
2
3public class PaymentRequest
4{
5    public string Method { get; set; } = "";
6    public string? CardNumber { get; set; }
7    public string? Iban { get; set; }
8}
9
10public class PaymentRequestValidator : AbstractValidator<PaymentRequest>
11{
12    public PaymentRequestValidator()
13    {
14        RuleFor(x => x.Method).NotEmpty();
15
16        When(x => x.Method == "card", () =>
17        {
18            RuleFor(x => x.CardNumber)
19                .NotEmpty()
20                .CreditCard();
21        })
22        .Otherwise(() =>
23        {
24            RuleFor(x => x.CardNumber).Empty();
25        });
26
27        When(x => x.Method == "bank", () =>
28        {
29            RuleFor(x => x.Iban)
30                .NotEmpty()
31                .Length(15, 34);
32        });
33    }
34}

This style keeps the conditional branches close to the rules they control.

Use Rule-Level Conditions for Small Cases

If the condition affects only one rule, attach it directly:

csharp
RuleFor(x => x.Iban)
    .Matches("^[A-Z0-9]+$")
    .When(x => x.Method == "bank");

That is often cleaner than opening a whole When block for a single rule.

Rule-level conditions are especially helpful when the validator already has a strong main structure and you only need one exception. They keep the happy path visible instead of burying everything inside nested blocks.

Bring in External Context When Needed

Sometimes the condition depends on runtime context, not just model fields. FluentValidation lets you use validation context data for that:

csharp
1public class SignupRequest
2{
3    public string Email { get; set; } = "";
4    public string? ReferralCode { get; set; }
5}
6
7public class SignupValidator : AbstractValidator<SignupRequest>
8{
9    public SignupValidator()
10    {
11        RuleFor(x => x.Email).NotEmpty().EmailAddress();
12
13        RuleFor(x => x.ReferralCode)
14            .NotEmpty()
15            .When((model, context) =>
16                context.RootContextData.TryGetValue("RequireReferral", out var value) &&
17                value is bool required && required);
18    }
19}

That is useful for tenant-specific or endpoint-specific policies without hardcoding everything into the model.

Another option in larger validators is to use rule sets for workflow stages and conditions for smaller branch logic inside each rule set. That combination often scales better than putting every business path into one giant validator constructor.

Apply Conditions to Nested Validators Carefully

Conditional logic also matters when a child object is only valid in certain situations. You can gate a nested validator the same way you gate a simple property rule:

csharp
RuleFor(x => x.CardDetails)
    .SetValidator(new CardDetailsValidator())
    .When(x => x.Method == "card");

This keeps validation aligned with the actual object graph. If CardDetails should only exist for card payments, the validator communicates that rule directly instead of forcing the child validator to guess whether it should run.

Common Pitfalls

The biggest mistake is spreading the same condition across many unrelated rules until the validator becomes hard to read. Group related conditional rules together so the intent stays obvious.

Another common issue is writing conditions that conflict with each other. If one rule requires a field and another branch expects it to be empty, make sure the branches are truly mutually exclusive.

People also use conditional validation as a substitute for a better model shape. Sometimes separate request types or rule sets are cleaner than one object with many optional fields and many conditions.

Finally, test both sides of every condition. Conditional rules are easy to write and easy to forget to exercise.

Summary

  • Use When, Unless, and Otherwise to express conditional validation clearly.
  • Apply conditions at the rule level when only one rule depends on them.
  • Use validation context data when the condition comes from runtime policy rather than the model itself.
  • Keep related conditional rules grouped together for readability.
  • Add tests for both the active and inactive sides of each validation condition.

Course illustration
Course illustration

All Rights Reserved.