JAAvila.FluentOperations 1.1.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package JAAvila.FluentOperations --version 1.1.0
                    
NuGet\Install-Package JAAvila.FluentOperations -Version 1.1.0
                    
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="JAAvila.FluentOperations" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="JAAvila.FluentOperations" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="JAAvila.FluentOperations" />
                    
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 JAAvila.FluentOperations --version 1.1.0
                    
#r "nuget: JAAvila.FluentOperations, 1.1.0"
                    
#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 JAAvila.FluentOperations@1.1.0
                    
#: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=JAAvila.FluentOperations&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=JAAvila.FluentOperations&version=1.1.0
                    
Install as a Cake Tool

JAAvila.FluentOperations

Tests .NET C# License

A fluent validation and testing library for .NET that unifies inline assertions and model validation through Quality Blueprints with automatic scenario inference.

Packages

Package Description
JAAvila.FluentOperations Core library — inline .Test() assertions and Quality Blueprints
JAAvila.FluentOperations.DependencyInjection DI extensions for blueprint registration
JAAvila.FluentOperations.AspNetCore ASP.NET Core action filters for automatic validation
JAAvila.FluentOperations.MediatR MediatR pipeline behavior for request validation
JAAvila.FluentOperations.MinimalApi Minimal API endpoint filters for automatic validation
JAAvila.FluentOperations.Analyzers Roslyn analyzers for compile-time usage checks (FO001, FO003)
dotnet add package JAAvila.FluentOperations

Quick Start

Inline Assertions

using JAAvila.FluentOperations;

// Strings
"user@example.com".Test().NotBeNull().NotBeEmpty().Contain("@").BeEmail();

// Numerics
42.Test().BePositive().BeLessThan(100).BeEven();
3.14m.Test().BeInRange(0m, 10m).HavePrecision(2);

// Date/Time
DateTime.Now.Test().BeToday().BeWeekday();

// Collections
new[] { 1, 2, 3 }.Test().HaveCount(3).Contain(2).BeInAscendingOrder();

// Exceptions
Action act = () => throw new InvalidOperationException("fail");
act.Test().Throw<InvalidOperationException>().WithMessage("*fail*");

// Assertion Scope (batch failures)
using (new AssertionScope())
{
    "".Test().NotBeEmpty();
    0.Test().BePositive();
}
// Throws single exception with all failures combined

Quality Blueprints (Model Validation)

public class UserBlueprint : QualityBlueprint<User>
{
    public UserBlueprint()
    {
        using (Define())
        {
            For(x => x.Email).Test().NotBeNull().NotBeEmpty().BeEmail();
            For(x => x.Name).Test().NotBeNull().HaveMinLength(2);
            For(x => x.Age).Test().BeInRange(18, 120);
        }
    }
}

var blueprint = new UserBlueprint();
var report = blueprint.Check(new User { Email = "", Name = "J", Age = 15 });

if (!report.IsValid)
{
    foreach (var failure in report.Failures)
        Console.WriteLine($"{failure.PropertyName}: {failure.Message}");
}

Supported Types

Category Types Operations
String string? 45+ ops: Be, NotBe, BeEmpty, NotBeNullOrEmpty, Contain, NotContain, Match (regex), BeEmail, BeUrl, BeJson, BeXml, BeSemVer, BeIPAddress, BeCreditCard, MatchWildcard, ContainAll, ContainAny, HaveLengthGreaterThan, HaveLengthLessThan, BeOneOf, NotBeOneOf...
Boolean bool, bool? Be, BeTrue, BeFalse, BeAllTrue, BeAllFalse, Imply, HaveValue
Numeric int, long, decimal, double, float (+ nullable) Be, BePositive, BeNegative, BeZero, BeGreaterThan, BeLessThan, BeInRange, BeOneOf, BeDivisibleBy, BeEven, BeOdd, BeApproximately (decimal, double, float), HavePrecision, BeNaN, BeInfinity, BeFinite...
Date/Time DateTime, DateOnly, TimeOnly, TimeSpan, DateTimeOffset (+ nullable) Be, BeAfter, BeBefore, BeInRange, BeToday, BeInThePast, BeInTheFuture, BeWeekday, BeCloseTo, NotBeCloseTo, HaveYear...
Collections IEnumerable<T>, T[], Dictionary<TKey,TValue> HaveCount, Contain, ContainAll, ContainInOrder, OnlyContain, BeSubsetOf, BeInAscendingOrder, BeInAscendingOrder(keySelector), AllSatisfy, BeUnique, HaveElementAt, SatisfyRespectively, HaveMinCount, Inspect, ExtractSingle, ContainEquivalentOf, NotContainEquivalentOf, NotContainNull, HaveCountBetween, ContainKeys (dictionary)...
Special Guid, Guid?, Enum<T>, Uri? Be, BeEmpty, BeDefined, HaveFlag, HaveScheme, BeAbsolute...
Object object? BeNull, NotBeNull, BeSameAs, BeOfType, BeAssignableTo, BeEquivalentTo (with builder options)
Action Action, Func<Task> Throw, ThrowExactly, NotThrow, NotThrowAfter, CompleteWithinAsync

Blueprint Features

Scenario-Based Validation

// Define your own marker interfaces — Scenario<T> works with any interface
public interface ICreateOrder { }
public interface IUpdateOrder { }

public class OrderBlueprint : QualityBlueprint<Order>
{
    public OrderBlueprint()
    {
        using (Define())
        {
            For(x => x.OrderId).Test().NotBeNull(); // Always applied

            Scenario<ICreateOrder>(() =>
            {
                For(x => x.CustomerId).Test().NotBeNull();
            });

            Scenario<IUpdateOrder>(() =>
            {
                For(x => x.Status).Test().NotBe("Closed");
            });
        }
    }
}

// Automatic scenario inference from interfaces
public class OrderForCreate : Order, ICreateOrder { }
var report = blueprint.Check(new OrderForCreate { ... }); // global + create rules

Severity, Error Codes, Custom Messages

using (Define())
{
    For(x => x.Email, new RuleConfig
    {
        Severity = Severity.Error,
        ErrorCode = "EMAIL_REQUIRED",
        CustomMessage = "A valid email is required"
    }).Test().NotBeNull().BeEmail();

    For(x => x.Nickname, new RuleConfig { Severity = Severity.Warning })
        .Test().HaveMinLength(3);
}

var report = blueprint.Check(model);
report.HasErrors;    // Only Severity.Error
report.HasWarnings;  // Only Severity.Warning
report.Errors;       // Filtered list

Async Validation

using (Define())
{
    ForAsync(x => x.Email, async email =>
    {
        return await emailService.IsAvailableAsync(email);
    }, "Email is already taken");
}

var report = await blueprint.CheckAsync(model);

Cross-Property Comparison

using (Define())
{
    ForCompare(x => x.StartDate, x => x.EndDate).BeLessThan();
    ForCompare(x => x.Password, x => x.ConfirmPassword).Be();
}

Conditional Validation

using (Define())
{
    When(
        condition: x => x.IsInternational,
        then: () =>
        {
            For(x => x.PassportNumber).Test().NotBeNull();
        },
        otherwise: () =>
        {
            For(x => x.NationalId).Test().NotBeNull();
        }
    );
}

Nested Objects, ForEach, Include

using (Define())
{
    // Nested object validation
    ForNested(x => x.Address, new AddressBlueprint());

    // Per-item collection validation
    ForEach(x => x.Items, new OrderItemBlueprint());

    // Compose blueprints
    Include(new BaseEntityBlueprint());
}

CascadeMode

public class StrictBlueprint : QualityBlueprint<Order>
{
    public StrictBlueprint()
    {
        CascadeMode = CascadeMode.StopOnFirstFailure;

        using (Define())
        {
            For(x => x.Email).Test().NotBeNull().BeEmail();
        }
    }
}

Transformation Rules

using (Define())
{
    // Validate a transformed value (same-type: trim whitespace)
    ForTransform(x => x.Email, email => email?.Trim())
        .Test().NotBeNull().NotBeEmpty();

    // Cross-type: transform DateTime → int, then validate as integer
    ForTransform(x => x.BirthDate, (DateTime d) => DateTime.Now.Year - d.Year)
        .Test().BeGreaterThanOrEqualTo(18);
}

Custom Validators

public class LuhnValidator : ICustomValidator<string?>
{
    public bool Validate(string? value) => /* Luhn algorithm */;
    public string FailureMessage => "Invalid card number";
}

using (Define())
{
    ForCustom(x => x.CardNumber, new LuhnValidator());
}

Blueprint Assertions (Test-to-Production Bridge)

// Use blueprints as direct assertions in tests
var blueprint = new UserBlueprint();
blueprint.Assert(user);           // Throws if invalid
await blueprint.AssertAsync(user); // Async version

// Works with AssertionScope
using (new AssertionScope())
{
    blueprint.Assert(user1);  // Failures accumulate
    blueprint.Assert(user2);  // All reported together
}

// In production: returns report (unchanged)
var report = blueprint.Check(user);

Framework Integration

ASP.NET Core

dotnet add package JAAvila.FluentOperations.AspNetCore
builder.Services.AddControllers(options =>
{
    options.AddBlueprintValidation();
});
builder.Services.AddBlueprintValidationFilter();
builder.Services.AddSingleton<OrderBlueprint>();

MediatR

dotnet add package JAAvila.FluentOperations.MediatR
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(assembly));
builder.Services.AddBlueprintValidation();
builder.Services.AddSingleton<CreateOrderCommandBlueprint>();

Minimal API

dotnet add package JAAvila.FluentOperations.MinimalApi
builder.Services.AddSingleton<CreateOrderBlueprint>();

app.MapPost("/orders", (CreateOrderRequest request) => Results.Ok(request))
    .WithBlueprint<CreateOrderRequest, CreateOrderBlueprint>();
// Returns RFC 7807 ValidationProblem on failure

See Integration Guide for full examples.

Localization

Configure localized validation messages for any culture:

FluentOperationsConfig.Configure(c =>
{
    c.Localization.Provider = new DictionaryMessageProvider()
        .AddMessage("String.BeEmail", "es-ES", "Se esperaba un correo electronico valido.");
    c.Localization.Culture = new CultureInfo("es-ES");
});

Built-in providers: DictionaryMessageProvider (in-memory) and ResourceManagerProvider (.resx files).

Roslyn Analyzers

dotnet add package JAAvila.FluentOperations.Analyzers

Compile-time detection of common mistakes:

Diagnostic Description
FO001 .Test() called without chaining an assertion operation
FO003 Define() in Blueprint without using statement

Test Framework Configuration

FluentOperations auto-detects your test framework (NUnit, xUnit, MSTest, TUnit). Override explicitly:

FluentOperationsConfig.Configure(c =>
    c.TestFramework.Framework = TestFramework.None); // Production mode

Documentation

Project Stats

  • 6335+ tests across NUnit test projects
  • 730+ validators covering 20+ data types
  • 37 operation managers with fluent chaining
  • 6 NuGet packages (core + 3 integrations + MinimalApi + Analyzers)
  • Performance optimized with lazy initialization, caching, and zero-allocation patterns

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Author

Built by JAAvila

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (8)

Showing the top 5 NuGet packages that depend on JAAvila.FluentOperations:

Package Downloads
JAAvila.FluentOperations.MediatR

MediatR integration for JAAvila.FluentOperations Quality Blueprints. Provides pipeline behaviors for automatic request validation.

JAAvila.FluentOperations.DependencyInjection

Dependency Injection extensions for JAAvila.FluentOperations Quality Blueprints.

JAAvila.FluentOperations.AspNetCore

ASP.NET Core integration for JAAvila.FluentOperations Quality Blueprints. Provides action filters for automatic model validation.

JAAvila.FluentOperations.MinimalApi

Minimal API integration for JAAvila.FluentOperations Quality Blueprints. Provides endpoint filters for automatic model validation with RFC 7807 Problem Details.

JAAvila.FluentOperations.Grpc

gRPC server interceptor integration for JAAvila.FluentOperations Quality Blueprints. Provides automatic request validation with structured error metadata in gRPC trailers.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.5.2 96 4/3/2026
1.5.1 99 3/29/2026
1.5.0 215 3/29/2026
1.3.0 201 3/26/2026
1.2.0 84 3/21/2026
1.1.0 208 3/21/2026
1.0.3-alpha.0.2 40 3/18/2026
1.0.3-alpha.0.1 40 3/18/2026
1.0.2 92 3/17/2026
1.0.2-alpha.0.4 49 3/14/2026
1.0.2-alpha.0.3 110 3/14/2026
1.0.2-alpha.0.2 46 3/14/2026
1.0.2-alpha.0.1 45 3/14/2026
1.0.1 136 3/9/2026
1.0.0 137 3/8/2026