Source-generated, attribute-based alternative to FluentValidation for .NET: no reflection, 0 bytes allocated when a model is valid, and NativeAOT-ready.
- Nothing allocated on the valid path. A passing validation allocates 0 B, asserted by allocation regression tests for flat, nested and collection models. FluentValidation 12 allocates 696–3,528 B for the same models.
- 22.7–145.0× faster than FluentValidation 12 on the valid path, measured side by side in the same BenchmarkDotNet run.
- Works under NativeAOT and trimming. The validator is plain generated C#, with no reflection or expression trees; CI publishes and runs a NativeAOT sample on every build.
- Mistakes surface at compile time. A numeric rule on a string, an
[IsInEnum]on a non-enum or a[CustomValidation]method with the wrong signature fails the build, not a request.
dotnet add package ZeroAlloc.ValidationThe source generator ships inside this package; nothing else is required.
using System;
using ZeroAlloc.Validation;
var validator = new CreateOrderRequestValidator(); // generated at build time
var result = validator.Validate(new CreateOrderRequest { Reference = "", Amount = 0, Email = "nope" });
if (!result.IsValid)
foreach (ref readonly var f in result.Failures)
Console.WriteLine($"{f.PropertyName}: {f.ErrorMessage}");
[Validate]
public class CreateOrderRequest
{
[NotEmpty, MaxLength(50)] public string Reference { get; set; } = "";
[GreaterThan(0)] public decimal Amount { get; set; }
[NotEmpty, EmailAddress] public string Email { get; set; } = "";
}Mark a model with [Validate] and the generator writes CreateOrderRequestValidator for you. Running it prints:
Reference: Reference must not be empty.
Amount: Amount must be greater than 0.
Email: Email must be a valid email address.
- 20 built-in rules as attributes: emptiness, equality, ranges, lengths, regex, e-mail, enums, precision and scale.
- Your own rules through reusable attributes, inline
[Must]predicates or[CustomValidation]methods. - Asynchronous rules, awaited by the generated
ValidateAsync. - Nested objects and collections, with property paths such as
Items[0].Skuin the failures. - Rules inherited from base types, conditional rules with
When,Unlessand[SkipWhen], and short-circuiting with[StopOnFirstFailure]. - Per-rule severity,
Error,WarningorInfo, and custom error messages with placeholders. - Pipeline behaviors that wrap validation calls, without a per-call allocation.
Optional packages:
ZeroAlloc.Validation.AspNetCore: validates request models automatically and returns HTTP 422 on failure.ZeroAlloc.Validation.Inject: registers every validator in DI with oneAddZeroAllocValidators()call.ZeroAlloc.Validation.Options: validatesMicrosoft.Extensions.Optionsat startup withValidateWithZeroAlloc().ZeroAlloc.Validation.Testing: fluent assertions for unit-testing validators.
| Scenario | Speedup vs FluentValidation 12 | Allocated (valid) |
|---|---|---|
| Flat model | 22.7× | 0 B vs 664 B |
| Nested model | 100.3× | 0 B vs 1,488 B |
| Collection, 3 items | 145.0× | 0 B vs 3,528 B |
Valid path, FluentValidation 12.2.0, measured 2026-10-21. Allocations are exact. Speed is shown as same-run ratios; absolute timings are not published. On the invalid path ZeroAlloc is 20.9–129.0× faster and allocates 13.0–77.5× less. See Performance for the full results.
- Documentation: validation.zeroalloc.net, including the attribute reference and the diagnostics list.
- Upgrading from v1: Migrating to v2 and the changelog.
Related ZeroAlloc packages:
- ZeroAlloc.Mediator (docs): its
ZeroAlloc.Mediator.Validationpackage runs these validators on every request before the handler. - ZeroAlloc.Results (docs): a Mediator request that returns
Result<T, ValidationError>gets a failed validation back as a failure result, not an exception. - ZeroAlloc.Pipeline (docs): the pipeline-behavior model this library's validator behaviors are built on.
- ZeroAlloc.Authorization (docs): plugs into the same Mediator pipeline and runs before validation, so a denied caller sees no validation errors.
Contributions are welcome: open an issue or a pull request. Licensed under MIT.