Introduction
Compile-time generated validation for your requests, with no runtime rule registration.
Immediate.Validations generates validation code at compile time. You annotate a type with [Validate], declare that it implements IValidationTarget<T>, and decorate its properties with
validator attributes; the source generator emits a Validate method that runs those checks in
order and returns a ValidationResult. There is no runtime rule registry or expression-tree
traversal, and nothing to register in the container. Custom-validator metadata such as DefaultMessage may still be accessed reflectively. Mistakes — a missing attribute, a validator
applied to a type it cannot handle, a behavior pipeline that forgot validation — are reported as
compiler diagnostics rather than as surprises in production.
Installation
dotnet add package Immediate.Validations Immediate.Validations depends on Immediate.Handlers, which comes in transitively — you do
not need to install it separately. Supported target frameworks are net8.0 through net10.0.
The ValidationBehavior<,> integration additionally requires an Immediate.Handlers pipeline;
see Integrating with Immediate.Handlers.
A minimal example
using Immediate.Validations.Shared;
[Validate]
public sealed partial record Query : IValidationTarget<Query>
{
[GreaterThan(0)]
public required int Id { get; init; }
[NotEmpty]
public required string Name { get; init; }
}var result = Query.Validate(new Query { Id = 0, Name = "" });
// result.IsValid == false
// result.Errors[0] => { PropertyName = "Id", ErrorMessage = "'Id' must be greater than '0'." }
// result.Errors[1] => { PropertyName = "Name", ErrorMessage = "'Name' must not be empty." } The type must be partial — the generator writes the other half.
What it replaces
If you are coming from FluentValidation, the mental shift is that rules live on the type as
attributes rather than in a separate AbstractValidator<T> class, and they are resolved by the
compiler rather than by assembly scanning. Anything attributes cannot express goes in an AdditionalValidations method on the type
itself, which gives you the same imperative freedom without a second class.
If you are coming from System.ComponentModel.DataAnnotations, the surface will look familiar,
but validation is executed by generated code instead of Validator.TryValidateObject’s
reflection-based traversal, and nested objects and collections are recursed into automatically.
The generated validation path supports trimming and AOT; preserve any reflectively accessed
custom-validator metadata when trimming.
Where to go next
[Validate], IValidationTarget, and the automatic checks you get for free.
All 15 validator attributes, with signatures and default messages.
How child objects and collections are walked, and how error paths are named.
The analyzer-enforced contract for your own validator attributes.
Register ValidationBehavior so every request is validated automatically.
Every IV diagnostic, its severity, and how to fix it.