CodingFlow/fluent-validation
Minimal, easy to use fluent validations API for C#.
CodingFlow Fluent Validation
Minimal, easy to use fluent validations API inspired by FluentValidation.
When you need to validate any type, even primitives in an easy and direct way, this library fits the bill. FluentValidation by Jeremy Skinner requires creating a separate validator class to register validation rules, and then instantiating the validator class. This library on the other hand, let's you add validation directly. This library is also ~ 20% faster performance-wise (See performance benchmark).
Usage
After installing the nuget package from Nuget.org, add this using statement to the file where you want to validate:
using static CodingFlow.FluentValidation.Validations;Then you can add validation like this:
var input = 11;
var result = RuleFor(input)
.BetweenInclusive(4, 6)
.Result();
// Check results
bool isValid = result.IsValid;
var errors = result.Errors;Validators
There are several built-in validators available out-of-the-box. You can also provide your own validation logic via the predicate validator (aka Must).
NotEmpty Validator
Ensures the value is not null for reference types or a default value for value types. For strings, ensures it is not null, an empty string, or only whitespace.
RuleFor(input)
.NotEmpty()
.Result();BetweenInclusive Validator
Ensures a number of any type (int, float, double, etc.) is greater than or equal to a minimum and less than or equal to a maximum.
RuleFor(input)
.BetweenInclusive(6, 14)
.Result();BetweenExclusive Validator
Ensures a number of any type (int, float, double, etc.) is greater than a minimum and less than a maximum.
RuleFor(input)
.BetweenExclusive(6, 14)
.Result();Equal Validator
Ensures the input is considered equal to the provided value. For reference types it checks if the two references are to the same instance (reference equality). For value types, it checks it the types and values are the same (value equality).
RuleFor(input)
.Equal(8)
.Result();MinimumLength Validator
Ensures the string has a minimum length.
RuleFor(input)
.MinimumLength(5) // Must be at least 5 characters long.
.Result();MaximumLength Validator
Ensures the string has a maximum length.
RuleFor(input)
.MaximumLength(5) // Must be at most 5 characters long.
.Result();Regular Expression Validator
Aka Matches, ensure the string passes a regular expression test.
RuleFor(input)
.Matches("cat")
.Result();Predicate Validator
The predicate (aka Must) validator allows you to provide your own validation logic by providing a delegate.
RuleFor(input)
.Must(input => input == 7)
.Result();IsGuid Validator
Ensures the string can be parsed into a valid GUID.
RuleFor(input)
.IsGuid()
.Result();Customizing
Custom Error Messages
The WithMessage method can be used to change the validation error message for a validator.
RuleFor(input)
.Equal(8).WithMessage("The two numbers are not equal.")
.Result();Integrations
Vogen
Extensions to integrate with Vogen validation methods.
To get started, install the Vogen extensions nuget package, CodingFlow.FluentValidation.VogenExtensions.
To get the final result of the fluent validation chain, call VogenResult() instead of Result():
[ValueObject]
public readonly partial struct Age
{
public static Validation Validate(int value)
{
return RuleFor(value)
.BetweenInclusive(0, 200)
.VogenResult();
}
}Performance Benchmark Comparison with FluentValidation
Benchmark of inclusive between validator shows this library is ~ 20 - 25% faster
than FluentValidation in .NET 9 and .NET 10.
BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.7462/25H2/2025Update/HudsonValley2)
Intel Core Ultra 5 245KF 4.20GHz, 1 CPU, 14 logical and 14 physical cores
.NET SDK 10.0.101
[Host] : .NET 10.0.1 (10.0.1, 10.0.125.57005), X64 RyuJIT x86-64-v3
.NET 10.0 : .NET 10.0.1 (10.0.1, 10.0.125.57005), X64 RyuJIT x86-64-v3
.NET 8.0 : .NET 8.0.22 (8.0.22, 8.0.2225.52707), X64 RyuJIT x86-64-v3
.NET 9.0 : .NET 9.0.11 (9.0.11, 9.0.1125.51716), X64 RyuJIT x86-64-v3
| Method | Job | Runtime | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
|---|---|---|---|---|---|---|---|---|
| CodingFlow | .NET 10.0 | .NET 10.0 | 34.70 ns | 0.338 ns | 0.316 ns | 0.0235 | - | 296 B |
| FluentValidation | .NET 10.0 | .NET 10.0 | 46.42 ns | 0.228 ns | 0.213 ns | 0.0471 | 0.0001 | 592 B |
| CodingFlow | .NET 8.0 | .NET 8.0 | 47.44 ns | 0.538 ns | 0.503 ns | 0.0287 | - | 360 B |
| FluentValidation | .NET 8.0 | .NET 8.0 | 52.02 ns | 0.387 ns | 0.323 ns | 0.0471 | 0.0001 | 592 B |
| CodingFlow | .NET 9.0 | .NET 9.0 | 37.48 ns | 0.149 ns | 0.139 ns | 0.0287 | - | 360 B |
| FluentValidation | .NET 9.0 | .NET 9.0 | 48.27 ns | 0.499 ns | 0.467 ns | 0.0471 | 0.0001 | 592 B |
Benchmark code:
[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net10_0)]
[SimpleJob(RuntimeMoniker.Net90)]
[SimpleJob(RuntimeMoniker.Net80)]
public class BetweenBenchmark
{
private readonly int input = 5;
private readonly IntegerValidator validator = new();
[Benchmark]
public bool CodingFlow()
{
return RuleFor(input)
.BetweenInclusive(1, 7)
.Result()
.IsValid;
}
[Benchmark]
public bool FluentValidation()
{
return validator.Validate(input)
.IsValid;
}
}FluentValidation validator:
internal class IntegerValidator : AbstractValidator<int>
{
public IntegerValidator()
{
RuleFor(x => x).InclusiveBetween(1, 7);
}
}