The Specification Pattern in .NET cover image

The Specification Pattern in .NET

By Julian Krause · Monday, December 1, 2025 · ~2 min read

The Specification pattern encapsulates a business rule into a reusable, composable object. In .NET, it pairs naturally with Entity Framework Core to build complex queries from simple, testable building blocks. When your query logic starts getting duplicated across services and controllers, specifications bring it under control.

The Basic Specification

A specification is, at its core, a predicate — a function that takes an entity and returns true or false. In EF Core, we express this as an Expression<Func<T, bool>> so the database can evaluate it.

public abstract class Specification<T>
{
    public abstract Expression<Func<T, bool>> ToExpression();

    public bool IsSatisfiedBy(T entity)
    {
        var predicate = ToExpression().Compile();
        return predicate(entity);
    }
}

public class ActiveOrderSpecification : Specification<Order>
{
    public override Expression<Func<Order, bool>> ToExpression()
        => order => order.Status == OrderStatus.Active
                  && order.ExpiresAt > DateTimeOffset.UtcNow;
}

Composing Specifications

The real power comes from composing specifications. An And specification combines two specifications, both of which must be satisfied. An Or specification requires at least one. This lets you build complex business rules from simple, well-tested pieces.

public class AndSpecification<T>(Specification<T> left, Specification<T> right)
    : Specification<T>
{
    public override Expression<Func<T, bool>> ToExpression()
    {
        var leftExpr = left.ToExpression();
        var rightExpr = right.ToExpression();

        var parameter = Expression.Parameter(typeof(T));
        var body = Expression.AndAlso(
            Expression.Invoke(leftExpr, parameter),
            Expression.Invoke(rightExpr, parameter));

        return Expression.Lambda<Func<T, bool>>(body, parameter);
    }
}

// Extension methods for fluent composition
public static class SpecificationExtensions
{
    public static Specification<T> And<T>(this Specification<T> left, Specification<T> right)
        => new AndSpecification<T>(left, right);

    public static Specification<T> Or<T>(this Specification<T> left, Specification<T> right)
        => new OrSpecification<T>(left, right);

    public static Specification<T> Not<T>(this Specification<T> spec)
        => new NotSpecification<T>(spec);
}

Using Specifications with EF Core

Specifications integrate cleanly with EF Core queries via extension methods on IQueryable:

public static class QueryableExtensions
{
    public static IQueryable<T> Where<T>(
        this IQueryable<T> queryable,
        Specification<T> specification)
    {
        return queryable.Where(specification.ToExpression());
    }
}

// Usage in a handler
var activeHighValue = new ActiveOrderSpecification()
    .And(new HighValueOrderSpecification(threshold: 1000m));

var orders = await _context.Orders
    .Where(activeHighValue)
    .OrderByDescending(o => o.CreatedAt)
    .Take(50)
    .ToListAsync(ct);

When to Use Specifications

Specifications shine when the same business rule is used in multiple places — queries, validation, authorization checks. If a rule like "an order is eligible for cancellation" appears in a controller, a background job, and a domain service, extract it into a specification. One definition, one set of tests, used everywhere.

For simple one-off queries, specifications add unnecessary abstraction. A direct LINQ expression in the handler is perfectly fine. Reach for specifications when duplication appears, not before.


Comments (2)

Hannah Friday, March 6, 2026 5:08 PM

I have a question: does this also apply to older versions?

Ben Friday, March 6, 2026 8:08 PM

Interesting thought, thanks for adding that.

Clara Saturday, March 7, 2026 8:08 PM

Thanks for your comment — glad it helped!

Anna Saturday, March 7, 2026 5:08 PM

Thanks for sharing — very helpful.

Leave a Comment