All Identity Element
Overview
Section titled “Overview”Specification<T>.All is a special Specification that satisfies all entities. In this chapter, you will learn how All serves as the identity element of the AND operation and how it makes dynamic filter construction concise.
All & X == X— Like 1 * x == x in mathematics, All is a neutral element that has no effect in AND operations.
Learning Objectives
Section titled “Learning Objectives”Key Learning Objectives
Section titled “Key Learning Objectives”-
Understanding the Null Object Pattern
Allis a Null Object representing “no conditions”- Start with
Allinstead of null checks to safely accumulate conditions
-
Identity Element Concept
All & X == X,X & All == X(identity element of AND operation)- When the
&operator detectsAll, it returns the other operand as-is without creating a new object
-
Dynamic Filter Pattern
- Incrementally assembling Specifications based on user input or search criteria
- Pattern of starting with
Alland adding conditions with&=
What You Will Verify Through Practice
Section titled “What You Will Verify Through Practice”All.IsSatisfiedBy()always returns trueAll & Xreturns the same reference asX(ReferenceEquals)- Dynamic filter construction and execution
Key Concepts
Section titled “Key Concepts”Null Object Pattern
Section titled “Null Object Pattern”When representing “no conditions” in programming, null is commonly used:
// Using null - null check required every timeSpecification<Product>? spec = null;
if (hasCategory) spec = spec == null ? new ProductCategorySpec(cat) : spec.And(new ProductCategorySpec(cat));
if (hasPrice) spec = spec == null ? new ProductPriceRangeSpec(min, max) : spec.And(new ProductPriceRangeSpec(min, max));
// Null check also required at execution timevar results = spec == null ? products : products.Where(p => spec.IsSatisfiedBy(p));Using All eliminates null checks:
// Using All - no null checks neededvar spec = Specification<Product>.All;
if (hasCategory) spec &= new ProductCategorySpec(cat);
if (hasPrice) spec &= new ProductPriceRangeSpec(min, max);
// Execution - All returns all products, conditions apply filteringvar results = products.Where(p => spec.IsSatisfiedBy(p));Reference Optimization of the Identity Element
Section titled “Reference Optimization of the Identity Element”The & operator detects All and returns the other operand as-is without creating a new object:
var all = Specification<Product>.All;var inStock = new ProductInStockSpec();
var result = all & inStock;ReferenceEquals(result, inStock); // true - not a new object!This is not merely an optimization but a code expression of the mathematical definition of an identity element. Just as 1 * x == x in multiplication, All & X == X in AND operations.
Dynamic Filter Pattern
Section titled “Dynamic Filter Pattern”This is the most commonly used pattern in practice. When only user-selected conditions on a search screen need to be applied:
var spec = Specification<Product>.All;
if (categoryFilter is not null) spec &= new ProductCategorySpec(categoryFilter);
if (nameFilter is not null) spec &= new ProductNameContainsSpec(nameFilter);
if (onlyInStock) spec &= new ProductInStockSpec();
var results = products.Where(p => spec.IsSatisfiedBy(p));If no conditions are selected, spec remains All, so all products are returned.
Project Description
Section titled “Project Description”Project Structure
Section titled “Project Structure”AllIdentity/├── Program.cs├── Product.cs├── SampleProducts.cs├── Specifications/│ ├── ProductInStockSpec.cs│ ├── ProductPriceRangeSpec.cs│ ├── ProductCategorySpec.cs│ └── ProductNameContainsSpec.cs└── AllIdentity.csproj
AllIdentity.Tests.Unit/├── AllIdentityTests.cs├── Using.cs├── xunit.runner.json└── AllIdentity.Tests.Unit.csprojCore Code
Section titled “Core Code”Dynamic Filter Construction
Section titled “Dynamic Filter Construction”var spec = Specification<Product>.All;
if (categoryFilter is not null) spec &= new ProductCategorySpec(categoryFilter);
if (onlyInStock) spec &= new ProductInStockSpec();
var results = SampleProducts.All.Where(p => spec.IsSatisfiedBy(p));Summary at a Glance
Section titled “Summary at a Glance”All Identity Element Properties
Section titled “All Identity Element Properties”| Property | Value/Behavior |
|---|---|
All.IsSatisfiedBy(x) | Always true |
All.IsAll | true |
All & X | Returns X (ReferenceEquals) |
X & All | Returns X (ReferenceEquals) |
null vs All Comparison
Section titled “null vs All Comparison”| Aspect | null Approach | All Approach |
|---|---|---|
| Initial Value | null | Specification<T>.All |
| Adding Conditions | Null check then And or assign | &= operator |
| Execution | Null check then filter or return all | Just filter (All returns everything) |
| Safety | NullReferenceException risk | null-safe |
Q1: Is All a singleton?
Section titled “Q1: Is All a singleton?”A: Yes, AllSpecification<T> is implemented as a per-type singleton through a static readonly instance. Specification<Product>.All always returns the same object.
Q2: Can All be used in Or operations too?
Section titled “Q2: Can All be used in Or operations too?”A: Technically possible, but the semantics are different. All | X is always true (since All satisfies everything), making it effectively the same as All. All is designed as the identity element of the AND operation; in Or operations, it becomes an absorbing element.
Q3: Why doesn’t the And() method have identity element optimization?
Section titled “Q3: Why doesn’t the And() method have identity element optimization?”A: The And() method simply returns new AndSpecification<T>(this, other). The identity element optimization is only included in the & operator. This is a design decision to maintain the simplicity of the method while ensuring performance and reference equality when using operators.
Q4: Can Or conditions also be added in dynamic filters?
Section titled “Q4: Can Or conditions also be added in dynamic filters?”A: Yes, you can use the |= operator. However, when mixing And and Or, be careful about operator precedence. If complex compositions are needed, it is better to clarify intent using intermediate variables.
Q5: How is this pattern used in practice?
Section titled “Q5: How is this pattern used in practice?”A: It is used everywhere that requires dynamically constructing queries based on user input, such as search APIs, filtering UIs, and report conditions. In Part 3, you will learn how to generate dynamic SQL queries by combining this with EF Core.
In Part 1, we built the foundations of Specification — condition encapsulation, composition, operators, and the All identity element. However, the Specifications so far only work on in-memory collections. In Part 2, we introduce Expression Trees to make Specifications usable with ORMs like EF Core.