Crabalidator 1.0.4

dotnet add package Crabalidator --version 1.0.4
                    
NuGet\Install-Package Crabalidator -Version 1.0.4
                    
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="Crabalidator" Version="1.0.4" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Crabalidator" Version="1.0.4" />
                    
Directory.Packages.props
<PackageReference Include="Crabalidator" />
                    
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 Crabalidator --version 1.0.4
                    
#r "nuget: Crabalidator, 1.0.4"
                    
#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 Crabalidator@1.0.4
                    
#: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=Crabalidator&version=1.0.4
                    
Install as a Cake Addin
#tool nuget:?package=Crabalidator&version=1.0.4
                    
Install as a Cake Tool

Crabalidator

Build NuGet License

Crabalidator is a high-performance .NET validation library built on top of DynaBee runtime code generation.

Crabalidator is designed for applications that want FluentValidation-style validators, but prefer compiled runtime validation plans over reflection-heavy execution paths. Validators describe rules through a familiar fluent API, Crabalidator turns those rules into backend-neutral validation plans, and DynaBee generates executable validation methods for hot sync paths.

What Crabalidator Does

  • Configures validators with CrabValidator<T> and RuleFor(...).
  • Supports common sync rules such as NotEmpty, string rules, collection count rules, comparisons, equality, membership, and custom Must(...) predicates.
  • Supports property-level When(...) and Unless(...) conditions.
  • Supports Cascade(CascadeMode.Stop) for fail-fast property validation.
  • Supports inferred nested object validation through ValidateNested(...).
  • Supports inferred collection element validation through ValidateEach(...).
  • Supports async custom rules through MustAsync(...).
  • Supports async nested validators and cancellation.
  • Integrates with Microsoft.Extensions.DependencyInjection.
  • Provides ICrabalidator, typed IValidator<T>, and typed IAsyncValidator<T> runtime APIs.
  • Provides registered validator diagnostics and readable validation plan output through DescribePlan(...).
  • Uses DynaBee-generated method bodies and invokers for optimized sync validation paths.
  • Optimizes custom Must(...) predicates by emitting direct DynaBee calls for visible delegate methods.
  • Includes BenchmarkDotNet coverage against FluentValidation baselines.

Design Goals

Crabalidator is intentionally split from the generation engine.

Crabalidator owns:

  • validation configuration
  • validation planning
  • rule semantics
  • diagnostics
  • dependency injection
  • runtime validator registration
  • public validation APIs

DynaBee owns:

  • generated type creation
  • generated method body creation
  • generated method invocation
  • low-level runtime code generation details

This boundary keeps Crabalidator focused on validation behavior while allowing DynaBee to evolve as a general-purpose runtime generation engine.

Requirements

  • .NET SDK 10.0+ recommended for development.
  • The library multi-targets net8.0, net9.0, and net10.0.

Installation

Install Crabalidator from NuGet:

dotnet add package Crabalidator --version 1.0.3

For local development, reference the project directly or use the solution in this repository.

Quick Start

Define a validator:

using Crabalidator;

public sealed class CustomerValidator : CrabValidator<Customer>
{
    public CustomerValidator()
    {
        RuleFor(x => x.Name)
            .Cascade(CascadeMode.Stop)
            .NotEmpty()
            .MinimumLength(3);

        RuleFor(x => x.Age)
            .GreaterThanOrEqualTo(18);

        RuleFor(x => x.Email)
            .NotEmpty()
            .Must(value => value.Contains('@'));
    }
}

public sealed class Customer
{
    public string Name { get; set; }

    public int Age { get; set; }

    public string Email { get; set; }
}

Built-In Rules

Crabalidator exposes common validators as extension methods in the base Crabalidator namespace:

RuleFor(x => x.Name)
    .NotEmpty()
    .MinimumLength(3)
    .MaximumLength(80)
    .StartsWith("CR")
    .Contains("AB")
    .Matches("^CRAB");

RuleFor(x => x.Email)
    .NotNull()
    .EmailAddress();

RuleFor(x => x.Age)
    .NotEmpty()
    .InclusiveBetween(18, 99);

RuleFor(x => x.Items)
    .NotEmpty()
    .MinimumCount(1)
    .MaximumCount(10);

RuleFor(x => x.Status)
    .In("ACTIVE", "PENDING")
    .NotIn("BLOCKED");

Custom Rules

Use Must(...) for project-specific rules:

RuleFor(x => x.Code)
    .Must(OrderRules.HasValidCode);

public static class OrderRules
{
    public static bool HasValidCode(string value)
        => value != null && value.StartsWith("CR", StringComparison.Ordinal);
}

When the predicate method is visible to generated code, Crabalidator emits a direct DynaBee call instead of going through Delegate.Invoke. Opaque lambdas and private methods still work and automatically fall back to delegate invocation.

Validate directly:

var validator = new CustomerValidator();
var result = validator.Validate(new Customer());

if (!result.IsValid)
{
    foreach (var failure in result.Errors)
    {
        Console.WriteLine($"{failure.PropertyName}: {failure.ErrorMessage}");
    }
}

Dependency Injection

Register Crabalidator with the assemblies that contain validators:

using Crabalidator;
using Crabalidator.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();

services.AddCrabalidator(typeof(CustomerValidator).Assembly);

var provider = services.BuildServiceProvider();
var validator = provider.GetRequiredService<IValidator<Customer>>();

var result = validator.Validate(customer);

Applications that prefer a single service entry point can use ICrabalidator:

var crabalidator = provider.GetRequiredService<ICrabalidator>();

ValidationResult result = crabalidator.Validate(customer);

Conditions And Cascade

Conditions apply to the property rule chain:

RuleFor(x => x.ReferralCode)
    .NotEmpty()
    .When(x => x.RequiresReferral);

Use cascade stop to avoid running later validators for the same property after the first failure:

RuleFor(x => x.Name)
    .Cascade(CascadeMode.Stop)
    .NotEmpty()
    .MinimumLength(3);

Nested Validators

Validate child objects:

ValidateNested(x => x.Address);

Validate collection elements:

RuleFor(x => x.Items)
    .NotEmpty();

ValidateEach(x => x.Items);

Nested failures are returned with prefixed paths such as Address.PostalCode or Items[0].Sku.

When more than one validator exists for a nested model, choose the validator explicitly:

ValidateNestedWith<ShippingAddressValidator>(x => x.Address);
ValidateEachWith<StrictOrderItemValidator>(x => x.Items);

Async Validation

Use MustAsync(...) for async checks:

RuleFor(x => x.Username)
    .NotEmpty()
    .MustAsync(IsUsernameAvailableAsync);

static async ValueTask<bool> IsUsernameAvailableAsync(
    string username,
    CancellationToken cancellationToken)
{
    await Task.Delay(10, cancellationToken);
    return username != "taken";
}

Validators that contain async rules must be executed with ValidateAsync(...).

Testing

Install the testing helpers from NuGet:

dotnet add package Crabalidator.Testing --version 1.0.3

Register validators in a lightweight test service collection:

using Crabalidator.Testing;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();

services.AddCrabalidatorTesting(typeof(CustomerValidator).Assembly);

var provider = services.BuildServiceProvider();
var validator = provider.GetRequiredService<IAsyncValidator<Customer>>();

var result = await validator.ValidateAsync(customer);

External test hosts can use the adapter-friendly registration:

services.AddCrabalidatorTestingAdapter(typeof(CustomerValidator).Assembly);

Run a specific validator directly without building a service provider:

var result = await CrabalidatorTest
    .For<CustomerValidator>()
    .ValidateAsync(customer);

For unit tests that already construct the validator, use TestValidate(...) directly:

var result = new CreateHeroRequestValidator()
    .TestValidate(new CreateHeroRequest("", "", "", 101, CId.Empty));

result.ShouldHaveErrorFor(request => request.Alias);
result.ShouldHaveErrorFor(request => request.PowerLevel);
result.ShouldHaveErrorFor(request => request.TeamId);

For request validation through dependency injection, use the lightweight test host:

await using var host = await CrabalidatorTestHost
    .Create()
    .UseValidatorsFromAssembly(typeof(Constants).Assembly)
    .BuildAsync();

var result = await host.ValidateAsync(request);

Assert validation results:

result.ShouldBeValid();

result.ShouldHaveErrorFor<Customer>(x => x.Email);
result.ShouldHaveErrorMessage("Email is required.");
result.ShouldHaveErrorCode("customer.email.required");

Property assertions can be scoped to a single property and chained:

result
    .ShouldHaveErrorFor<Customer>(x => x.Email)
    .WithErrorCount(2)
    .WithMessage("Email is required.")
    .WithErrorCode("customer.email.required")
    .WithSeverity(ValidationSeverity.Error);

Results returned by CrabalidatorTest.For<TValidator>() are typed, so direct validator tests can use the shorter property assertion:

var result = await CrabalidatorTest
    .For<CustomerValidator>()
    .ValidateAsync(customer);

result.ShouldHaveErrorFor(x => x.Email);

Diagnostics

Crabalidator can describe registered validators and generated validation plans:

using Crabalidator.Diagnostics;

var diagnostics = provider.GetRequiredService<ICrabalidatorDiagnostics>();

Console.WriteLine(diagnostics.DescribeRegisteredValidators());
Console.WriteLine(diagnostics.DescribePlan<Customer>());

Samples And Benchmarks

Run the basic sample:

dotnet run --project samples/Crabalidator.Samples.Basic

Run the test suite:

dotnet test

Run benchmarks:

dotnet run --project benchmarks/Crabalidator.Benchmarks -c Release

Benchmark Snapshot

These numbers come from BenchmarkDotNet on Windows 11, .NET 8.0, Release mode, using the short benchmark job in this repository. Treat them as a local comparison point rather than a universal guarantee.

Built-In Extension Rules

Scenario Crabalidator FluentValidation Speedup Crabalidator Alloc FluentValidation Alloc
Valid model 265.4 ns 643.4 ns 2.4x 48 B 672 B
Invalid model 471.0 ns 9,427.9 ns 20.0x 1,160 B 20,584 B

Custom Must(...) Predicate

Scenario Crabalidator FluentValidation Speedup Crabalidator Alloc FluentValidation Alloc
Public static predicate, valid model 9.3 ns 134.4 ns 14.4x 0 B 600 B
Public static predicate, invalid model 22.9 ns 679.7 ns 29.7x 152 B 1,856 B
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 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 is compatible.  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 (2)

Showing the top 2 NuGet packages that depend on Crabalidator:

Package Downloads
TurtlePath.Crabalidator

Crabalidator adapter for TurtlePath validation contracts.

Crabalidator.Testing

Testing helpers for Crabalidator validators without a full application host.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.4 194 8/11/2026
1.0.3 401 8/7/2026
1.0.2 107 8/6/2026
1.0.1 460 7/22/2026
1.0.0 116 7/22/2026