Reference

Reference and boundaries

Review the public concepts, generated syntax, package responsibilities, diagnostics, translation errors, and deliberate non-goals of version one.

Fluent Specifications keeps a narrow center: an immutable, named Boolean tree over one candidate type. The surrounding packages add syntax, diagnostics, and infrastructure translation without changing that meaning.

Core concepts

Concept Responsibility
Spec<T> Immutable Boolean rule and composition surface
Spec.Define Create a named leaf from an expression
Spec.Always<T>() Explicit rule that always passes
Spec.Never<T>() Explicit rule that never passes
Spec.AllOf Conjunction over a single immutable snapshot
Spec.AnyOf Disjunction over a single immutable snapshot
Matches Fast, short-circuiting in-memory Boolean evaluation
Check Structured complete or short-circuit diagnostics
Named Add a domain boundary without discarding child rules
Accept Provider-facing traversal over the closed rule tree
Order.Search Generated, provider-neutral search entry point
Order.Rules Explicit generated rule catalog for dynamic composition
Order.Fields Strongly typed generated field catalog
Search<T> Immutable filter, ordering, and optional paging description
Page<T> Materialized results plus page and total metadata
IReadRepository<T> Optional provider-neutral materializing read contract

The retained node kinds are Always, Never, Leaf, Named, And, Or, and Not. AndNot, OrNot, AllOf, and AnyOf are construction conveniences that normalize to those nodes.

Generated language

Catalog member Generated connector form
HighPriority rule.And.HighPriority
Suspended rule.AndNot.Suspended
WorthAtLeast(int) rule.And.WorthAtLeast(10_000)
grouped or dynamic rule rule.And(otherRule)
[Expose] CanShip order.CanShip
inferred search rule Order.Search.Matching.CanShip
primary field ordering .Sorted.By.CreatedAt.Desc
tie-break ordering .Then.By.Id.Asc
one-based page .Page(2).OfSize(50)

The first version deliberately does not overload Boolean operators and does not implicitly convert between specifications, expressions, delegates, or Boolean values.

Project responsibilities

Project Owns Must not own
FluentSpecifications.Core Rule tree, immutable searches, generated-field descriptors, pages, evaluation, diagnostics, traversal contracts EF or query-provider types
FluentSpecifications.Generators Catalog discovery and C# 14 rule, field, and search extension members Runtime query execution
FluentSpecifications.Expressions Parameter-rebound expression plans IQueryable application
FluentSpecifications.Repositories Provider-neutral materializing read contract Provider or deferred-query types
FluentSpecifications.EntityFrameworkCore Relational preflight and IReadRepository<T> implementation Application repository policy
FluentSpecifications.Docs Roslyn symbol extraction and Markdown synchronization Business-rule execution

Dependencies point inward. Core has no reference to the repository contract or a provider adapter. The repository contract depends only on Core; EF Core depends on that contract and supplies one implementation.

NuGet packages

The 1.2.0 suite publishes four coordinated packages:

Package Contents Direct package dependencies
DanMarshall.FluentSpecifications Core runtime and source generator None
DanMarshall.FluentSpecifications.Repositories Provider-neutral IReadRepository<T> contract Starter package
DanMarshall.FluentSpecifications.Expressions Parameter-rebound expression translator Starter package
DanMarshall.FluentSpecifications.EntityFrameworkCore Relational executor and repository implementation Starter, repository, expressions, and EF Core Relational

The starter package began at 1.0.0 and retains zero third-party package dependencies. The optional extensions join at 1.2.0 and do not become transitive dependencies of the starter. Installing the EF package brings the repository and expression assemblies plus Microsoft’s relational EF runtime; install the starter directly as well when its source generator is required.

Maintainers select one coordinated SemVer explicitly in every packable project. The package-suite script reads each effective PackageVersion, rejects any mismatch, and produces all .nupkg and .snupkg artifacts together. Existing package lines accept only the immediate next patch, minor, or major transition; a new extension may begin at the current suite version.

Publication is an explicitly dispatched GitHub Actions workflow whose requested version must match the projects. Before requesting a credential, it compares every package ID with NuGet.org. Idempotent retries allow recovery from a partially completed suite publication, while NuGet’s immutable package versions still prevent an artifact from being replaced.

Releases use NuGet.org Trusted Publishing. The publisher requests a short-lived OIDC credential immediately before pushing the packages, so no long-lived NuGet API key is stored in the repository or its CI settings.

Failure types

Type Meaning
RuleFailure A predicate returned false as a normal business result
EvaluationError A predicate threw during diagnostic evaluation
SpecificationEvaluationException Fast Boolean evaluation could not produce a result
TranslationError Infrastructure could not prepare a provider plan
SpecificationTranslationException A failed plan was requested for execution

Translation errors carry a stable code, node path, and rule ID when a specific leaf is responsible. They do not contain candidate values.

Generator diagnostics

Code Meaning
FSPEC001 The specification catalog shape is invalid
FSPEC002 An exposed property would hide an instance member
FSPEC003 Generated extension properties require C# 14
FSPEC004 A rule member shape cannot be represented safely
FSPEC005 More than one catalog tries to generate inferred search language for an entity
FSPEC006 An entity member would hide Search, Rules, or Fields
FSPEC007 A catalog member would collide with a generated search-support type

Search generation is opt-in for 1.x source compatibility. Mark the one catalog that owns an entity’s inferred search language with GenerateSearch = true on its SpecificationSet<T> attribute. Other catalogs keep generating their normal specification connectors. Generated fields include effective inherited public readable members, while indexers, static members, and inaccessible getters are omitted. Existing catalog members named SearchRoot, RuleCatalog, SearchRuleCatalog, or FieldCatalog are diagnosed because those names are reserved for generated support types. If a field name collides with an inherited object member on the selector, use the explicit dynamic form .Sorted.By[Order.Fields.ToString] or .Then.By[Order.Fields.ToString].

Deliberate non-goals

A specification is not a container for:

  • sorting, paging, projection, joins, or includes;
  • tracking, split-query, caching, or provider flags;
  • mutations, commands, or async workflows;
  • arbitrary validation UI state;
  • a promise that every .NET expression translates everywhere; or
  • a CRUD repository framework.

Those exclusions keep a.Or.b meaningful. Sorting and paging therefore live in the separate immutable Search<T> description. Searches still exclude projection, joins, includes, tracking, split-query settings, and provider objects. The optional IReadRepository<T> contract standardizes only the small materializing read surface already represented by specifications and searches.

Current status

The repository targets C# 14 and .NET 10. The coordinated 1.2.0 suite keeps the starter dependency-free while making the repository contract, expression translator, and EF Core implementation independently installable. The checked-in specification and executable tests define its behavioral contract.