FluentEnforce 2.1.0
dotnet add package FluentEnforce --version 2.1.0
NuGet\Install-Package FluentEnforce -Version 2.1.0
<PackageReference Include="FluentEnforce" Version="2.1.0" />
<PackageVersion Include="FluentEnforce" Version="2.1.0" />
<PackageReference Include="FluentEnforce" />
paket add FluentEnforce --version 2.1.0
#r "nuget: FluentEnforce, 2.1.0"
#:package FluentEnforce@2.1.0
#addin nuget:?package=FluentEnforce&version=2.1.0
#tool nuget:?package=FluentEnforce&version=2.1.0
FluentEnforce
Lightweight, expressive parameter validation for .NET: 100+ built-in validations, fluent API, extensible architecture. Fail fast at method boundaries with zero boilerplate.
Table of contents
Why FluentEnforce
- Readable validation chains:
email.EnforceNotNull().NotWhiteSpace().MatchesEmail()
- 100+ unique built-in checks: strings, numbers, dates, collections, enums, GUIDs, and more
- Dual exception modes: standard
ArgumentException
(and its inherited types) or your custom exceptions - Method boundary defense: validate early, fail fast
- Zero overhead: aggressive inlining (This is removed for now, for more optimization in a later subversion, but the JIT is already smart enough to handle those methods), compiled regex patterns
- AOT-friendly and fast: AOT compatibility is enabled, optimized equality, no runtime code emission or reflection
Install
dotnet add package FluentEnforce
Targets: net9.0
.
Quick tour
Basic validation
// Static approach
Enforce.NotNull(email)
.NotWhiteSpace()
.MatchesEmail();
Enforce.That(itemsList)
.HasMinCount(minItemsToShip)
.AllMatch(item => item.CanBeShipped);
Enforce.That(endDate).GreaterThan(startDate);
// Extension approach (equivalent)
email.EnforceNotNull()
.NotWhiteSpace()
.MatchesEmail();
itemsList.Enforce()
.HasMinCount(minItemsToShip)
.AllMatch(item => item.CanBeShipped);
endDate.Enforce().GreaterThan(startDate);
Note: For educational purposes, you will see me switch between using the regular static approach and extension approach. For readability, it's preferable to stick to one approach in a single code block. More about this in the documentation, in the
Best Practices
section.
Constructor defense
public sealed class Email
{
public string Value { get; }
public Email(string value)
{
Value = Enforce.NotNull(value)
.NotWhiteSpace()
.MatchesEmail()
.Value; // Get validated value back
// [Note that `.Value` is optional. The Enforce type already has implicit conversion to the wrapped type]
}
}
Custom exceptions
email.EnforceNotNull(() => new DomainException("Email required"))
.MatchesEmail(() => new DomainException("Invalid email format"));
Rich validations
// Numbers
Enforce.That(age).InRange(18, 100, RangeBounds.LeftInclusive);
Enforce.That(price).Positive().LessThan(1000);
// Collections
Enforce.NotNull(items).NotEmpty().HasMinCount(1).HasMaxCount(10);
// Dates
Enforce.That(birthDate).InPast();
Enforce.That(appointment).InFuture();
// Extensible (For repeated validations)
Enforce.That(order1).Valid(); // And we define the extension Valid() method below
Enforce.That(order2).Valid();
static class OrderEnforceExtensions
{
public static Enforce<Order> Valid(this Enforce<Order> enforce)
{
var order = enforce.Value;
var paramName = enforce.ParamName;
order.Items.Enforce(paramName).NotEmpty();
order.Total.EnforceNotNull(paramName).Positive();
return enforce;
}
// Alternative approach.
// The above method is equavalent to:
public static Enforce<Order> Valid(this Enforce<Order> enforce)
{
var order = enforce.Value;
var paramName = enforce.ParamName;
if (!order.Items.Any())
throw new ArgumentException("Order must have items", paramName);
if(order.Total <= 0)
throw new ArgumentOutOfRangeException(paramName, order.Total, "Order total must be positive");
return enforce;
}
}
Validation categories
Core types (147 methods)
- String (41): length, content, patterns, regex patterns, email/URL/phone/IP/GUID formats
- Numeric (15): comparisons, signs, number types, divisibility
- Collections (13): count, contained items, uniqueness, predicates (with overloads for the core collection types)
- DateTime (8 + 5 numeric comparisons): past/future/today checks, numeric comparisons, with
TimeProvider
support - General (5): equality, null, in/not-in sets (With some overloads)
- TimeSpan (6 + 5 numeric comparisons): signs, numeric comparisons
- Character (9): letter/digit/whitespace/symbol/special
- Boolean (2): true/false
- GUID (2): empty/not-empty
- Enum (1): defined values
Each validation has both ArgumentException
and custom exception variants (in addition to overloads for the collection types).
Documentation
This README stays intentionally brief. Full documentation lives in the docs:
- Index: docs index
- Guide: docs/guide.md
- API reference: docs/validation-reference.md
- Examples: docs/examples.md
- Best practices: docs/best-practices.md
- International validation: docs/international.md
- Contributing: docs/contributing.md
Links
- NuGet: FluentEnforce on NuGet
- Repository: github.com/ymjaber/fluent-enforce
- License: MIT (LICENSE)
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. 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. |
-
net9.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.
Version | Downloads | Last Updated |
---|---|---|
2.1.0 | 41 | 8/15/2025 |