Skip to content

Pragmatic.Validation

Zero-allocation, source-generated validation for .NET 10.

Validation in .NET scatters across layers and styles. Inline if checks bury rules inside business logic and get duplicated. DataAnnotations uses reflection on every call, can’t do async, and handles cross-property rules awkwardly. FluentValidation is expressive but runtime-only, and the validator class lives apart from the model it validates. In all three, rules are scattered, reflection-based, or disconnected from the model — and none integrates natively with a DomainAction pipeline or entity change tracking.

Declare rules as attributes on the model. The generator produces the exact validation code at compile time — no reflection, no runtime expression evaluation, no separate validator class.

public partial class CreateReservationRequest
{
[Required, NotWhiteSpace]
public string GuestName { get; init; } = "";
[Required, Email]
public string Email { get; init; } = "";
[FutureDate]
public DateTimeOffset CheckIn { get; init; }
[GreaterThanProperty(nameof(CheckIn))]
public DateTimeOffset CheckOut { get; init; }
[Positive, Range(1, 20)]
public int NumberOfGuests { get; init; } = 1;
}

The generator emits ISyncValidator.Validate() with inline if statements. For database checks, implement IAsyncValidator<T> with [Validator] — the generator wires up DI and a CompositeValidator<T>. Add [Validate] on a DomainAction and the pipeline runs validation before Execute().

  • Level 1 (input) — validates input DTOs before any database access: sync attributes first, then async validators (uniqueness, external checks).
  • Level 2 (entity) — validates entity invariants after a mutation is applied, before persistence; change-aware, so only modified properties are re-validated.
Terminal window
dotnet add package Pragmatic.Validation
dotnet add package Pragmatic.SourceGenerator # the analyzer that generates the validators

All attributes live in Pragmatic.Validation.Attributes. Full details + semantics: Attribute Reference.

CategoryAttributesUse when
Presence[Required], [NotEmpty], [NotWhiteSpace]Value must exist / not be blank
String[MinLength], [MaxLength], [Length], [Regex], [Email], [Url], [Phone], [CreditCard], [Guid]String format or length
Numeric[Range], [GreaterThan(OrEqual)], [LessThan(OrEqual)], [Positive], [Negative]Constraining numbers
Date[FutureDate], [PastDate]Date must be past/future
Collection[MinCount], [MaxCount], [Count], [ValidateElements]Collection size / per-element
Cross-property[EqualTo], [NotEqualTo], [GreaterThanProperty], [LessThanProperty], [RequiredIf], [RequiredIfNot]Compare / conditionally require
Enum/Set[ValidEnum], [OneOf]Must be a defined enum member / allowed value
Custom (sync)extend ValidationAttributeCustom synchronous logic
Asyncimplement IAsyncValidator<T> + [Validator]Database / external service checks

Decorate a partial class; the generator emits Validate():

using Pragmatic.Validation.Attributes;
public partial class RegisterGuestRequest
{
[Required, NotWhiteSpace] public string FirstName { get; init; } = "";
[Required, Email] public string Email { get; init; } = "";
[Range(18, 120)] public int Age { get; init; }
}
// Run it directly…
var result = request.Validate();
// …or let a DomainAction run L1+L2 automatically:
[DomainAction]
[Validate]
public partial class RegisterGuest : DomainAction<Guid> { /* ... */ }

Full walkthrough: Getting Started.

Stable within the 0.8 preview — the attribute set, sync/async validators, and the L1/L2 pipeline are settled. See the roadmap.

| Concepts | L1/L2 model, sync vs async, change-aware validation, decision guide | | Getting Started | Your first validated DTO and the [Validate] pipeline | | Attribute Reference | Every built-in attribute, parameters, generated code, diagnostics | | Custom Validators | IAsyncValidator<T>, [Validator], CompositeValidator<T>, custom ValidationAttribute | | Common Mistakes | The most frequent validation pitfalls | | Troubleshooting | Problem/solution guide with diagnostics |

  • .NET 10.0+
  • Pragmatic.SourceGenerator analyzer

Part of the Pragmatic.Design ecosystem — see Licensing. Pragmatic.Validation is MIT-licensed.