PANiXiDA.Core.SpecificationPattern 1.0.2

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

PANiXiDA.Core.SpecificationPattern

PANiXiDA.Core.SpecificationPattern is a .NET library for implementing the Specification pattern with reusable business rules, composable predicates, and filtering support for in-memory collections and query providers.

It is designed for domain and application code that needs explicit, testable rules which can be evaluated against objects or converted to expression trees for querying.

Status

CI NuGet NuGet downloads Target Framework License

Overview

The package provides a small public API for creating and combining specifications:

  • define business rules as ISpecification<T> or Specification<T>;
  • create expression-backed specifications with SpecificationFactory.Create;
  • create always-true and always-false specifications with SpecificationFactory.All and SpecificationFactory.None;
  • compose specifications with And, Or, and Not;
  • filter IQueryable<T> sources by expression;
  • filter IEnumerable<T> sources by compiled predicate.

Composition implementations are internal. Consumers work through the public abstraction, base class, factory, and extension methods instead of depending on concrete composition classes.

Requirements

  • .NET 10 SDK
  • A project targeting net10.0 or a compatible target framework

Installation

Package Manager:

dotnet add package PANiXiDA.Core.SpecificationPattern

PackageReference:

<ItemGroup>
  <PackageReference Include="PANiXiDA.Core.SpecificationPattern" Version="1.0.1" />
</ItemGroup>

For projects using central package management, define the version in Directory.Packages.props.

<ItemGroup>
  <PackageVersion Include="PANiXiDA.Core.SpecificationPattern" Version="1.0.1" />
</ItemGroup>

Quick Start

Create a specification from an expression and use it to evaluate a candidate object:

using PANiXiDA.Core.SpecificationPattern.Core;
using PANiXiDA.Core.SpecificationPattern.Factories;

public sealed class User
{
    public bool IsActive { get; init; }

    public bool IsBlocked { get; init; }

    public int Age { get; init; }
}

Specification<User> activeAdult = SpecificationFactory.Create<User>(
    user => user.IsActive && user.Age >= 18);

bool isSatisfied = activeAdult.IsSatisfiedBy(new User
{
    IsActive = true,
    Age = 21
});

Usage

Custom specifications

Use Specification<T> when a rule deserves a named type:

using System.Linq.Expressions;
using PANiXiDA.Core.SpecificationPattern.Core;

public sealed class ActiveUserSpecification : Specification<User>
{
    public override Expression<Func<User, bool>> ToExpression()
    {
        return user => user.IsActive;
    }
}

Named specifications are useful when the same rule is reused across domain logic, application services, and query scenarios.

Composition

Specifications derived from Specification<T> can be combined with And, Or, and Not.

using PANiXiDA.Core.SpecificationPattern.Core;
using PANiXiDA.Core.SpecificationPattern.Factories;

Specification<User> active = new ActiveUserSpecification();
Specification<User> adult = SpecificationFactory.Create<User>(user => user.Age >= 18);
Specification<User> blocked = SpecificationFactory.Create<User>(user => user.IsBlocked);

Specification<User> activeAdult = active.And(adult);
Specification<User> activeOrAdult = active.Or(adult);
Specification<User> allowedActiveAdult = active.And(adult).And(blocked.Not());

And and Or accept ISpecification<T> arguments, so custom implementations can participate in composition. The composition methods themselves are available on Specification<T>.

Queryable filtering

Use the IQueryable<T> extension when the source should receive the expression tree.

using PANiXiDA.Core.SpecificationPattern.Core;
using PANiXiDA.Core.SpecificationPattern.Extensions;
using PANiXiDA.Core.SpecificationPattern.Factories;
using System.Linq;

IQueryable<User> users = new[]
{
    new User { IsActive = true, Age = 21 },
    new User { IsActive = true, Age = 16 },
    new User { IsActive = false, Age = 30 }
}.AsQueryable();

Specification<User> activeAdult = SpecificationFactory
    .Create<User>(user => user.IsActive)
    .And(SpecificationFactory.Create<User>(user => user.Age >= 18));

IQueryable<User> query = users.Where(activeAdult);

This is intended for query providers such as Entity Framework Core, provided the expression can be translated by the provider.

Enumerable filtering

Use the IEnumerable<T> extension for in-memory collections.

using PANiXiDA.Core.SpecificationPattern.Core;
using PANiXiDA.Core.SpecificationPattern.Extensions;
using PANiXiDA.Core.SpecificationPattern.Factories;
using System.Collections.Generic;

IEnumerable<User> users = new[]
{
    new User { IsActive = true, Age = 21 },
    new User { IsActive = false, Age = 30 }
};

Specification<User> active = SpecificationFactory.Create<User>(user => user.IsActive);

IEnumerable<User> result = users.Where(active);

For IEnumerable<T>, the specification expression is compiled and evaluated as a predicate.

Public API

ISpecification<T>

Core abstraction for specification implementations.

bool IsSatisfiedBy(T candidate);
Expression<Func<T, bool>> ToExpression();

Specification<T>

Base class for reusable specifications.

  • caches the compiled predicate used by IsSatisfiedBy;
  • requires derived classes to implement ToExpression;
  • exposes And, Or, and Not composition methods.

SpecificationFactory

Factory for common specification creation scenarios.

Specification<T> All<T>();
Specification<T> None<T>();
Specification<T> Create<T>(Expression<Func<T, bool>> expression);

SpecificationQueryableExtensions

Filtering extensions for queryable and enumerable sources.

IQueryable<T> Where<T>(this IQueryable<T> query, ISpecification<T> specification);
IEnumerable<T> Where<T>(this IEnumerable<T> source, ISpecification<T> specification);

Behavior Notes

  • SpecificationFactory.Create throws ArgumentNullException when expression is null.
  • Specification<T>.And and Specification<T>.Or throw ArgumentNullException when specification is null.
  • Filtering extensions throw ArgumentNullException when the source or specification is null.
  • Not negates the current specification.
  • All<T> is satisfied by every candidate.
  • None<T> is not satisfied by any candidate.
  • Query provider compatibility depends on the expression used by the specification.

Configuration

The package does not require runtime configuration, environment variables, external services, or application settings.

Project Structure

.
|-- src/
|   `-- PANiXiDA.Core.SpecificationPattern/
|-- tests/
|   `-- PANiXiDA.Core.SpecificationPattern.UnitTests/
|-- Directory.Build.props
|-- Directory.Build.targets
|-- Directory.Packages.props
|-- global.json
|-- version.json
|-- LICENSE
`-- README.md

Development

Restore dependencies:

dotnet restore

Format code:

dotnet format

Build:

dotnet build --configuration Release

Run tests:

dotnet test --configuration Release

Pack:

dotnet pack --configuration Release

Full local validation:

dotnet restore
dotnet format
dotnet build --configuration Release
dotnet test --configuration Release
dotnet pack --configuration Release

Tooling

This repository uses:

  • .NET 10
  • Nullable reference types
  • Implicit usings
  • Central package management
  • Microsoft Testing Platform
  • xUnit v3
  • FluentAssertions
  • Nerdbank.GitVersioning

Contributing

When changing the package:

  • keep the public API small and intentional;
  • avoid unnecessary dependencies;
  • preserve existing naming and architecture;
  • update tests for meaningful behavior changes;
  • update this README when public behavior, public API, package metadata, or development workflow changes.

License

This project is licensed under the Apache-2.0 license.

See LICENSE for details.

Product Compatible and additional computed target framework versions.
.NET 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.
  • net10.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.2 110 4/19/2026