Start here

Getting started

Define a small catalog of named rules, compose them into fluent business language, and evaluate the result without exposing persistence details.

Fluent Specifications is built around one idea: a business rule should be easy to name, combine, run, and explain without becoming a query object.

Install

Install the .NET 10 SDK, open a terminal in the directory containing your project file, and run:

dotnet add package DanMarshall.FluentSpecifications

That adds the latest stable DanMarshall.FluentSpecifications release to the project. Pin --version 1.0.0 when you specifically need the initial release. If you edit project files directly, the equivalent is a PackageReference with that ID and version. Add using FluentSpecifications; (or a global using), then create a public static partial rule catalog marked with [SpecificationSet<T>] as shown below. The first build runs the included source generator automatically.

The package contains both the runtime and source generator and has zero third-party package dependencies. It relies only on .NET and compiler APIs supplied by Microsoft, and it does not bundle vendor runtime or compiler assemblies.

The smallest useful example

Create a static partial catalog for the domain type. A rule has a stable ID, a human name, and a typed predicate:

[SpecificationSet<QuickStartOrder>]
public static partial class QuickStartRules
{
    public static Spec<QuickStartOrder> Paid =>
        Spec.Define<QuickStartOrder>(
            "order.paid",
            "Paid",
            order => order.Paid);

    public static Spec<QuickStartOrder> Priority =>
        Spec.Define<QuickStartOrder>(
            "order.priority",
            "Priority",
            order => order.Priority);
}

The faint parameter labels shown in documentation examples are generated from the same Roslyn model that extracts the source. They are visual aids, like Rider inlay hints; they are not part of the copied C#.

Import the catalog once, then compose rules without operator overloads:

public static Spec<Order> ReadyToShip() =>
    CanShip.And.HighPriority.AndNot.Suspended;

The returned value is still a Spec<Order>. It can be reused, rendered, diagnosed, or passed to infrastructure without losing its rule tree.

Use a rule as domain language

Mark a zero-argument rule with [Expose] when it deserves to read like a Boolean property on the domain object:

[Expose]
public static Spec<Order> CanShip =>
    Paid
        .And(HasDeliveryAddress)
        .AndNot(Suspended)
        .Named(
            "order.can-ship",
            "Can ship",
            "The order is not ready to ship.");

The generator supplies the domain property:

public static bool ShouldDispatch(Order order)
{
    if (order.CanShip)
    {
        return true;
    }

    return false;
}

[Expose] is opt-in. It is best for important, argument-free domain concepts, not every small leaf rule.

What the package contains

DanMarshall.FluentSpecifications contains the immutable rule tree, evaluation and diagnostic APIs, plus the source generator that creates the fluent connector members. The generator runs as a compiler analyzer and is not a runtime dependency.

Provider translation remains deliberately separate. The repository contains expression and EF Core adapters, but the starter package does not pull EF Core or expose IQueryable to application code.

Where to go next

Read defining rules for naming and catalog design, then composition for grouping, negation, and parameterized rules. If the rules will reach a database, read the EF Core guide before assuming an in-memory match will translate. The prior-art notes explain the design lineage and where Fluent Specifications deliberately differs.