FluentEnforce 2.0.1

dotnet add package FluentEnforce --version 2.0.1
                    
NuGet\Install-Package FluentEnforce -Version 2.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FluentEnforce" Version="2.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FluentEnforce" Version="2.0.1" />
                    
Directory.Packages.props
<PackageReference Include="FluentEnforce" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add FluentEnforce --version 2.0.1
                    
#r "nuget: FluentEnforce, 2.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package FluentEnforce@2.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=FluentEnforce&version=2.0.1
                    
Install as a Cake Addin
#tool nuget:?package=FluentEnforce&version=2.0.1
                    
Install as a Cake Tool

FluentEnforce

NuGet License: MIT

FluentEnforce is a lightweight and expressive library for parameter validation in C#/.NET that provides a fluent API for enforcing preconditions. It enables defensive programming practices with readable validation chains, comprehensive built-in validations for common types, and support for both standard exceptions and custom exception types. Write cleaner, more maintainable code by validating inputs at method boundaries with minimal boilerplate.

Why FluentEnforce?

Traditional parameter validation in C# often leads to:

  • Verbose boilerplate - Repetitive if-throw statements clutter your methods
  • Inconsistent validation - Different developers write checks differently
  • Poor readability - Intent gets lost in implementation details
  • Scattered validation logic - No central place for common validations
  • Missing edge cases - Easy to forget important checks

FluentEnforce solves these problems by providing a consistent, readable, and comprehensive validation framework.

Key Features

  • 🎯 Fluent API - Chain validations for expressive, readable code
  • 💯 Comprehensive Validations - Built-in checks for strings, numbers, dates, GUIDs, and more
  • 🛡️ Defensive Programming - Fail fast at method boundaries
  • 🔗 Method Chaining - Combine multiple validations seamlessly
  • ⚡ Zero Overhead - Optimized with aggressive inlining
  • 🎨 Extensible - Easy to add custom validations
  • 📝 Automatic Parameter Names - Uses CallerArgumentExpression for better error messages
  • 🔄 Dual Exception Support - Use ArgumentException or custom exceptions
  • ✨ Clean Syntax - Minimal ceremony, maximum clarity
  • 🚀 Performance Focused - Designed for high-throughput scenarios

Installation

Core Package

dotnet add package FluentEnforce

Or via Package Manager:

Install-Package FluentEnforce

Quick Start

Basic Parameter Validation

using FluentEnforce;

public class UserService
{
    public User CreateUser(string email, string password, int age)
    {
        // Validate all parameters with fluent chains
        Enforce.NotNullOrWhiteSpace(email)
            .MatchesEmail();

        Enforce.NotNullOrWhiteSpace(password)
            .LongerThanOrEqualTo(8)
            .Contains("@", "Password must contain @");

        Enforce.That(age)
            .GreaterThanOrEqualTo(18)
            .LessThan(150);

        return new User(email, password, age);
    }
}

Constructor Validation

public class Email
{
    public string Value { get; }

    public Email(string value)
    {
        Value = Enforce.NotNullOrWhiteSpace(value)
            .MatchesEmail()
            .ShorterThanOrEqualTo(255)
            .Value; // Implicit conversion returns the validated value
    }
}

Custom Predicates

public void ProcessOrder(Order order, decimal discount)
{
    Enforce.NotNull(order)
        .Satisfies(o => o.Items.Any(), "Order must have items")
        .Satisfies(o => o.Total > 0, "Order total must be positive");

    Enforce.That(discount)
        .InRange(0, 100)
        .Satisfies(d => d % 5 == 0, "Discount must be in increments of 5");
}

Custom Exceptions

public class DomainException : Exception
{
    public DomainException(string message) : base(message) { }
}

public void ValidateEmail(string email)
{
    // Use custom exception instead of ArgumentException
    Enforce.That(email)
        .Satisfies(e => !string.IsNullOrWhiteSpace(e),
            () => new DomainException("Email is required"))
        .Satisfies(e => e.Contains("@"),
            () => new DomainException("Invalid email format"));
}

Common Patterns

1. Method Parameter Validation

public async Task<User> GetUserAsync(int userId, bool includeDetails)
{
    Enforce.That(userId).GreaterThan(0);

    var user = await repository.GetByIdAsync(userId);
    return includeDetails ? await LoadDetailsAsync(user) : user;
}

2. Domain Model Validation

public class Product
{
    public string Name { get; }
    public decimal Price { get; }
    public int StockLevel { get; }

    public Product(string name, decimal price, int stockLevel)
    {
        Name = Enforce.NotNullOrWhiteSpace(name)
            .ShorterThanOrEqualTo(100)
            .Value;

        Price = Enforce.That(price)
            .GreaterThan(0)
            .LessThanOrEqualTo(1_000_000)
            .Value;

        StockLevel = Enforce.That(stockLevel)
            .GreaterThanOrEqualTo(0)
            .Value;
    }
}

3. API Input Validation

[HttpPost]
public IActionResult CreateAccount([FromBody] CreateAccountRequest request)
{
    Enforce.NotNull(request);

    Enforce.NotNullOrWhiteSpace(request.Username)
        .LongerThanOrEqualTo(3)
        .ShorterThanOrEqualTo(20)
        .Matches("^[a-zA-Z0-9_]+$", "Username can only contain letters, numbers, and underscores");

    Enforce.NotNullOrWhiteSpace(request.Email)
        .MatchesEmail();

    Enforce.NotNullOrWhiteSpace(request.Password)
        .LongerThanOrEqualTo(8)
        .Matches("[A-Z]", "Password must contain uppercase letter")
        .Matches("[0-9]", "Password must contain number");

    // Process the valid request
    return Ok(accountService.CreateAccount(request));
}

4. Collection Validation

public void ProcessItems<T>(IEnumerable<T> items, int batchSize)
{
    Enforce.NotNull(items)
        .NotEmpty()
        .Satisfies(i => i.Count() <= 1000, "Too many items to process");

    Enforce.That(batchSize)
        .InRange(1, 100);

    // Process items in batches
}

Predefined Validations

FluentEnforce provides extensive built-in validations:

String Validations

  • NotEmpty(), Empty(), HasLength(), LongerThan(), ShorterThan()
  • Contains(), StartsWith(), EndsWith()
  • Matches() for regex patterns
  • MatchesEmail(), MatchesUrl(), MatchesPhoneNumber(), MatchesGuid()

Numeric Validations

  • GreaterThan(), LessThan(), InRange(), NotInRange()
  • Zero(), NotZero(), Positive(), Negative()
  • Even(), Odd(), DivisibleBy()

DateTime Validations

  • Past(), Future(), Today(), NotToday()
  • After(), Before(), InRange()
  • Weekday(), Weekend()

Boolean Validations

  • True(), False()

Enum Validations

  • Defined(), NotDefined()
  • HasFlag(), NotHaveFlag()

Collection Validations

  • Empty(), NotEmpty()
  • MinCount(), MaxCount(), ExactCount()

Best Practices

  1. Validate Early

    public void ProcessData(string input, int count)
    {
        // Validate at method entry
        Enforce.NotNullOrWhiteSpace(input);
        Enforce.That(count).Positive();
    
        // Now safe to use parameters
    }
    
  2. Use Descriptive Messages

    Enforce.That(age)
        .GreaterThanOrEqualTo(18, "User must be 18 or older")
        .LessThan(100, "Invalid age provided");
    
  3. Chain Related Validations

    Enforce.NotNullOrWhiteSpace(email)
        .MatchesEmail()
        .NotContain("tempmail", "Temporary emails not allowed")
        .ShorterThanOrEqualTo(255);
    
  4. Extract Complex Validations

    public static Enforce<string> ValidatePassword(this Enforce<string> enforce)
    {
        return enforce
            .NotEmpty()
            .LongerThanOrEqualTo(8)
            .Matches("[A-Z]", "Must contain uppercase")
            .Matches("[a-z]", "Must contain lowercase")
            .Matches("[0-9]", "Must contain number");
    }
    
    // Usage
    Enforce.That(password).ValidatePassword();
    

Performance

FluentEnforce is designed for high performance:

  • All validation methods use AggressiveInlining for zero-overhead abstractions
  • Predefined regex patterns are compiled and cached
  • No allocations for successful validations
  • Minimal overhead compared to manual if-throw statements

Real-World Example

public class OrderService
{
    private readonly IOrderRepository repository;
    private readonly IPaymentService paymentService;

    public async Task<Order> CreateOrderAsync(
        int customerId,
        List<OrderItem> items,
        string promoCode = null)
    {
        // Validate inputs
        Enforce.That(customerId).GreaterThan(0);

        Enforce.NotNull(items)
            .NotEmpty("Order must contain at least one item")
            .Satisfies(i => i.All(item => item.Quantity > 0),
                "All items must have positive quantity")
            .Satisfies(i => i.Sum(item => item.Price * item.Quantity) <= 10_000,
                "Order total exceeds maximum allowed");

        if (promoCode != null)
        {
            Enforce.That(promoCode)
                .Matches("^[A-Z0-9]{4,10}$", "Invalid promo code format");
        }

        // Business logic with validated inputs
        var order = new Order(customerId);
        foreach (var item in items)
        {
            order.AddItem(item);
        }

        if (promoCode != null)
        {
            await ApplyPromoCodeAsync(order, promoCode);
        }

        return await repository.SaveAsync(order);
    }
}

Documentation

For comprehensive documentation, visit the docs folder:

Contributing

We welcome contributions! Please see our Contributing Guide for details.

Acknowledgments

This library was inspired by Ardalis.GuardClauses by Steve Smith.

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • 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.0.1 88 7/28/2025
2.0.0 88 7/27/2025