FastValidate 1.1.0
dotnet add package FastValidate --version 1.1.0
NuGet\Install-Package FastValidate -Version 1.1.0
<PackageReference Include="FastValidate" Version="1.1.0" />
<PackageVersion Include="FastValidate" Version="1.1.0" />
<PackageReference Include="FastValidate" />
paket add FastValidate --version 1.1.0
#r "nuget: FastValidate, 1.1.0"
#:package FastValidate@1.1.0
#addin nuget:?package=FastValidate&version=1.1.0
#tool nuget:?package=FastValidate&version=1.1.0
FastValidate
Zero-reflection validation for .NET, powered by a Roslyn source generator.
Decorate your properties with validation attributes and FastValidate generates a compile-time Validate() extension method for you — no reflection, no runtime attribute scanning. That makes it safe for Native AOT and trimmed apps, where reflection-based validators typically break or need extra trimmer annotations.
Why
Most attribute-based validators (including reflection-driven ones) walk a type's properties at runtime with GetProperties() / GetCustomAttributes(). That's slow, allocates, and is exactly the kind of code Native AOT trimming can strip or fail to resolve. FastValidate moves that work to compile time: the generator reads your attributes once, at build, and emits plain, boring, fast C# that calls straight into the validation logic. Nothing to reflect over at runtime.
Measured against a reflection-based equivalent (FastValidate.Benchmarks, BenchmarkDotNet, Apple M3 Pro / .NET 10, 4 validated properties):
| Mean | Allocated | |
|---|---|---|
Reflection-based EntityValidator.ValidateEntity<T>() |
2,028.6 ns | 952 B |
FastValidate entity.Validate() |
578.4 ns | 192 B |
~3.5x faster, ~5x less memory allocated. Run it yourself: dotnet run -c Release --project FastValidate.Benchmarks.
Installation
dotnet add package FastValidate
That's it — one package. It contains both the attributes/validator library and the source generator.
Usage
using FastValidate;
public class SignupRequest
{
[EmailValidation]
public string Email { get; set; } = string.Empty;
[PhoneNumberValidation(Region.Spain)]
public string Phone { get; set; } = string.Empty;
[PasswordValidation(MinLength = 10, RequireSpecialChar = true)]
public string Password { get; set; } = string.Empty;
[LengthValidation(2, 50)]
public string DisplayName { get; set; } = string.Empty;
}
No base class, no partial keyword, no marker attribute on the class itself — just decorate the properties you want validated. At build time, FastValidate generates a Validate() extension method for SignupRequest:
var request = new SignupRequest { Email = "not-an-email", Phone = "612345678", Password = "weak", DisplayName = "A" };
List<ValidationResult> errors = request.Validate();
// errors: one entry per failing property (PropertyName + ErrorMessage),
// empty when everything is valid. Every property is checked — it doesn't
// stop at the first failure.
foreach (var error in errors)
Console.WriteLine($"{error.PropertyName}: {error.ErrorMessage}");
You can inspect the generated code yourself: enable <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> in your .csproj and look under obj/generated.
Attributes
| Attribute | Property type | Notes |
|---|---|---|
[EmailValidation] |
string |
|
[PhoneNumberValidation(Region)] |
string |
Region.USA, .UK, .Spain, .Ireland |
[PostalCodeValidation(Region)] |
string |
Same regions as phone |
[DateValidation] |
string |
Parsable date |
[UrlValidation] |
string |
Absolute http/https URLs |
[CreditCardValidation] |
string |
Luhn checksum |
[PasswordValidation] |
string |
MinLength, RequireUppercase, RequireLowercase, RequireDigit, RequireSpecialChar (named args, all optional) |
[IPAddressValidation] |
string |
IPv4 or IPv6 |
[RangeValidation(min, max)] |
any numeric | Inclusive range |
[LengthValidation(min, max)] |
string |
Inclusive length range |
[RegexValidation("pattern")] |
string |
Pattern embedded at compile time — no runtime registration |
[CustomValidation("name")] |
string |
Pattern registered at runtime via Validator.AddCustomValidation — see below |
[EqualsValidation(nameof(Other))] |
any | Must equal another property on the same type (e.g. confirm password) |
[ValidateObject] |
a type with its own FastValidate attributes | Recurses, prefixing nested errors (Address.PostalCode) |
[ValidateCollection] |
IEnumerable<T> of such a type |
Recurses per item, prefixing with the index (Items[2].Sku) |
Applying an attribute to an incompatible property type (e.g. [EmailValidation] on an int) is a compile error (FV001), not a silent no-op or a runtime surprise. [EqualsValidation] referencing a property that doesn't exist, or one with a different type, is likewise a compile error (FV002).
Custom rules
Prefer [RegexValidation] when the pattern is a compile-time constant — it needs no setup:
public class Product
{
[RegexValidation(@"^[A-Z]{3}-\d{4}$")]
public string Code { get; set; } = string.Empty;
}
Use [CustomValidation] when the pattern needs to be registered at startup (e.g. loaded from configuration):
Validator.AddCustomValidation("product-code", @"^[A-Z]{3}-\d{4}$");
public class Product
{
[CustomValidation("product-code")]
public string Code { get; set; } = string.Empty;
}
Cross-property and nested validation
public class AccountCreation
{
[PasswordValidation]
public string Password { get; set; } = string.Empty;
[EqualsValidation(nameof(Password))]
public string ConfirmPassword { get; set; } = string.Empty;
}
public class AddressInfo
{
[PostalCodeValidation(Region.Spain)]
public string PostalCode { get; set; } = string.Empty;
}
public class CustomerProfile
{
[EmailValidation]
public string Email { get; set; } = string.Empty;
[ValidateObject]
public AddressInfo Address { get; set; } = new();
[ValidateCollection]
public List<AddressInfo> ShippingAddresses { get; set; } = new();
}
// customerProfile.Validate() reports errors as "Address.PostalCode",
// "ShippingAddresses[1].PostalCode", etc. — one Validate() call, full graph.
Calling validators directly
Every attribute is backed by a plain static method on Validator, callable without any generated code:
ValidationResult result = Validator.IsValidEmail("user@example.com");
if (!result.IsValid)
Console.WriteLine(result.ErrorMessage);
Requirements
- Any SDK-style .NET project (the generator targets
netstandard2.0, so it works from .NET Framework 4.6.1+ through the latest .NET)
Development
git clone https://github.com/AdrianBailador/FastValidate.git
cd FastValidate
dotnet test
License
MIT — see LICENSE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Added DateValidation, RegexValidation (compile-time inline pattern), EqualsValidation (cross-property, e.g. confirm password, with FV002 diagnostic), ValidateObject and ValidateCollection (nested/recursive validation with prefixed property names). Added a BenchmarkDotNet comparison against reflection-based validation (~3.5x faster, ~5x less allocation).