ZeroAlloc.Specification 1.3.2

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

ZeroAlloc.Specification

NuGet Build License: MIT AOT GitHub Sponsors

Source-generated specification pattern for .NET, an Ardalis.Specification alternative: specs are structs, so composing and evaluating them allocates nothing, and the same spec still translates to SQL through EF Core.

Why ZeroAlloc.Specification

  • Composing and evaluating allocates nothing. And, Or and Not build readonly struct values. Composing and calling IsSatisfiedBy allocates 0 B, asserted by tests. Ardalis.Specification allocates 1,752 B to compose two specs.
  • About 320× faster for in-memory filtering than Ardalis.Specification, measured in the same run: 100 items in 49 ns instead of 15,599 ns, because nothing is compiled per call.
  • One spec for memory and the database. Every spec also exposes ToExpression(), so EF Core turns the composed spec into a WHERE clause.
  • Works under NativeAOT and trimming, with a NativeAOT smoke app published and run in CI. Mistakes such as [Specification] on a class or a missing partial are compile errors ZA001 to ZA003.

Install

dotnet add package ZeroAlloc.Specification

The source generator ships inside this package. Targets .NET 8 and later.

Quick start

using System.Linq.Expressions;
using ZeroAlloc.Specification;

User[] users = [new("Ann", true, 1500m), new("Bob", true, 200m), new("Cy", false, 3000m)];

var spec = new ActiveUserSpec().And(new BigSpenderSpec(1000m));        // a struct, 0 B

Console.WriteLine(users.Count(u => spec.IsSatisfiedBy(u)));             // 1, in memory
Console.WriteLine(spec.ToExpression());   // one combined expression for dbContext.Users.Where(...)

public sealed record User(string Name, bool IsActive, decimal TotalSpend);

[Specification]
public readonly partial struct ActiveUserSpec : ISpecification<User>
{
    public bool IsSatisfiedBy(User user) => user.IsActive;
    public Expression<Func<User, bool>> ToExpression() => u => u.IsActive;
}

[Specification]
public readonly partial struct BigSpenderSpec(decimal minSpend) : ISpecification<User>
{
    public bool IsSatisfiedBy(User user) => user.TotalSpend >= minSpend;
    public Expression<Func<User, bool>> ToExpression() { var min = minSpend; return u => u.TotalSpend >= min; }
}

The generator adds And, Or and Not to every [Specification] struct. Evaluation is a direct call on a struct that the JIT can inline, with no virtual dispatch.

What you get

  • Fluent composition: And, Or and Not, nested to any depth, without allocating.
  • Static builder: Spec.And, Spec.Or and Spec.Not when you want explicit type arguments.
  • In-memory evaluation through IsSatisfiedBy, for hot loops and domain rules.
  • EF Core translation through ToExpression(), which combines the specs' expression trees into one.
  • Compile-time checks that each spec is a readonly partial struct implementing ISpecification<T>.
  • Recipes for an EF Core repository, complex compositions and caching the expression of a stateless spec.

Building an expression tree with ToExpression() does allocate, as every expression tree does. Cache it for stateless specs.

Performance

Head-to-head vs Ardalis.Specification 9.3.1, .NET 10.0.13, BenchmarkDotNet v0.15.8.

Operation Ardalis.Specification ZA.Specification Speedup
Construct composed spec 305 ns / 792 B < 1 ns / 0 B > 300× faster, 0 B alloc
Compose two specs 821 ns / 1,752 B < 1 ns / 0 B > 800× faster, 0 B alloc
Evaluate over 100 items (in-memory) 15,599 ns / 4,743 B 49 ns / 0 B ~320× faster, 0 B alloc

Rows under 1 ns are below what BenchmarkDotNet can tell apart from an empty method, so those speedups are lower bounds. The machine was shared while measuring, so timings are approximate; the allocations are exact. For database queries the SQL provider dominates and the gap is invisible; for in-memory filtering it dominates. Full methodology: docs/performance.md.

Learn more

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.
  • net8.0

    • No dependencies.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on ZeroAlloc.Specification:

Package Downloads
Rag.NET.Abstractions

Interfaces and models for the Rag.NET library

Rag.NET

Modular RAG pipeline library for .NET — ingestion and retrieval pipeline builders, self-query, corrective RAG and conversation memory

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.2 406 10/16/2026
1.3.1 72 10/15/2026
1.3.0 395 10/10/2026
1.2.5 104 9/30/2026
1.2.4 142 9/26/2026
1.2.3 1,381 9/20/2026
1.2.2 213 9/20/2026
1.2.1 102 9/20/2026
1.2.0 108 9/19/2026
1.1.0 2,259 5/4/2026
0.1.0 146 3/18/2026