Vali-Flow.Core 2.0.2

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

Vali-Flow.Core - Fluent Expression Builder for .NET Validation

NuGet License .NET

Introduction πŸš€

Welcome to Vali-Flow.Core, the foundational library for the Vali-Flow ecosystem, providing a fluent API to build logical expressions for validation in .NET applications. Designed for seamless integration with LINQ and Entity Framework (EF), Vali-Flow.Core allows developers to construct complex validation conditions in a readable and type-safe manner. It supports a variety of data types and provides methods to build expressions for filtering entities, making it ideal for use in domain logic, repositories, or query pipelines.

Installation πŸ“¦

To add Vali-Flow.Core to your .NET project, install it via NuGet with the following command:

dotnet add package Vali-Flow.Core

Ensure your project targets a compatible .NET version (e.g., .NET 8.0 or 9.0) for optimal performance. Vali-Flow.Core is lightweight and dependency-free, making it easy to integrate into any .NET application.

Usage πŸ› οΈ

Vali-Flow.Core focuses on building expressions that can be used for validation or filtering. The library provides a fluent API through the ValiFlow<T> builder, which implements the IExpression<TBuilder, T> interface. You can construct conditions, combine them with logical operators (And, Or), and finalize the builder by generating an expression using Build() or BuildNegated().

Basic Example

Here’s how you can build a simple expression to filter Product entities:

using System.Linq.Expressions;
using Vali_Flow.Core.Builder;

var validator = new ValiFlow<Product>()
    .Add(p => p.Name != null)
    .And()
    .Add(p => p.Price, price => price > 0);

Expression<Func<Product, bool>> filter = validator.Build();

This expression can be used in a LINQ query or with Entity Framework to filter products where the name is not null and the price is greater than 0.

Key Methods πŸ“

Vali-Flow.Core provides methods to construct and finalize logical expressions. Below are the key methods for terminating the builder and generating expressions:

Build πŸ—οΈ

Generates a boolean expression (Expression<Func<T, bool>>) from the conditions defined in the builder. This expression can be used in LINQ queries or Entity Framework to filter entities.

var validator = new ValiFlow<Product>()
    .Add(p => p.Name != null)
    .And()
    .Add(p => p.Price, price => price > 0);

Expression<Func<Product, bool>> filter = validator.Build();

// Use the expression in a query
var validProducts = dbContext.Products.Where(filter).ToList();

BuildNegated πŸ”„

Generates a negated version of the expression produced by Build(). This is useful when you need to find entities that do not satisfy the defined conditions.

var validator = new ValiFlow<Product>()
    .Add(p => p.Name != null)
    .And()
    .Add(p => p.Price, price => price > 0);

Expression<Func<Product, bool>> negatedFilter = validator.BuildNegated();

// Use the negated expression in a query
var invalidProducts = dbContext.Products.Where(negatedFilter).ToList();

Building Complex Conditions 🧩

Vali-Flow.Core allows you to create complex expressions using logical operators (And, Or) and sub-groups (AddSubGroup). Here are some examples:

Using And and Or

Combine conditions with logical operators to create sophisticated filters:

var validator = new ValiFlow<Product>()
    .Add(p => p.Price, price => price > 0)
    .And()
    .Add(p => p.Name, name => name.StartsWith("A"))
    .Or()
    .Add(p => p.CreatedAt, date => date.Year == 2023);

Expression<Func<Product, bool>> filter = validator.Build();

This expression filters products where the price is greater than 0 AND the name starts with "A", OR the creation year is 2023.

Using AddSubGroup

Group conditions to create nested expressions:

var validator = new ValiFlow<Product>()
    .AddSubGroup(group => group
        .Add(p => p.Price, price => price > 0)
        .And()
        .Add(p => p.Name, name => name.Length > 3))
    .Or()
    .Add(p => p.IsActive, isActive => isActive == true);

Expression<Func<Product, bool>> filter = validator.Build();

This expression filters products where (price > 0 AND name length > 3) OR the product is active.

Comparison: Without vs. With Vali-Flow.Core βš–οΈ

Without Vali-Flow.Core (Manual Expression Building)

Manually building expressions can be cumbersome and error-prone:

Expression<Func<Product, bool>> filter = p =>
    p.Name != null &&
    p.Price > 0 &&
    p.CreatedAt.Date == DateTime.Today;

With Vali-Flow.Core (Fluent Expression Building)

Vali-Flow.Core simplifies the process with a fluent and readable API:

var validator = new ValiFlow<Product>()
    .Add(p => p.Name != null)
    .And()
    .Add(p => p.Price, price => price > 0)
    .And()
    .Add(p => p.CreatedAt, date => date.Date == DateTime.Today);

Expression<Func<Product, bool>> filter = validator.Build();

Features and Enhancements 🌟

What's New in v2.0.0 πŸš€

Breaking Changes

  • ValidationResult.ErrorsAbove() β†’ ErrorsAtOrAbove() β€” semantics use >= (at or above the given severity), not >. Update all call sites.
  • ValidationResult.ErrorsAtOrAbove() returns an IReadOnlyList<ValidationError> backed by AsReadOnly() β€” casting to List<T> will fail at runtime.

Performance

  • Validate() short-circuits OR groups β€” returns Ok() as soon as any group passes, without evaluating remaining groups.
  • ValidationResult.Warnings and CriticalErrors are now lazy β€” computed only when accessed, not on construction.
  • Regex cache in RegexMatch is now global (shared across all types) instead of per-closed-generic-type.
  • BuildWithGlobal() caches Build() β€” multiple calls on a frozen builder reuse the same expression tree.

API Improvements

  • CreateNestedBuilder<TProperty>() is now virtual β€” external subclasses of BaseExpression no longer required to override it.
  • Severity.Info behavior clarified: only appears in ValidationResult when the condition fails AND has an attached message.
  • WithMessage(Func<string>) factory must not return null β€” documented in IntelliSense.
  • All regex methods (IsEmail, IsUrl, RegexMatch, etc.) now document RegexMatchTimeoutException in their XML signatures.

Migration Guide

v1.x v2.0.0
result.ErrorsAbove(Severity.Warning) result.ErrorsAtOrAbove(Severity.Warning)

What's New in v2.0.0

Breaking Changes

  • Removed BeforeDate / AfterDate β€” use IsBefore / IsAfter (full DateTime/DateTimeOffset comparison including time-of-day).
  • Removed CountEquals β€” use Count (identical semantics).
  • IStringExpression<TBuilder,T> segregated into 4 focused sub-interfaces:
    • IStringLengthExpression β€” MinLength, MaxLength, ExactLength, LengthBetween
    • IStringContentExpression β€” StartsWith, EndsWith, Contains, EqualToIgnoreCase, IsOneOf
    • IStringStateExpression β€” IsNullOrEmpty, IsNullOrWhiteSpace, IsTrimmed, IsLowerCase, IsUpperCase, HasOnlyDigits/Letters/SpecialCharacters
    • IStringFormatExpression β€” IsEmail, IsUrl, IsGuid, IsJson, IsBase64, RegexMatch, MatchesWildcard, IsCreditCard, IsIPv4/IPv6, IsHexColor, IsSlug
    • If you implemented IStringExpression directly, implement the 4 sub-interfaces instead.

Performance

  • ConditionEntry<T> now compiles predicates lazily via Lazy<Func<T,bool>> β€” thread-safe without explicit locking. Validate() no longer acquires a per-condition lock.

Migration Guide

v1.x v2.0
BeforeDate(selector, date) IsBefore(selector, date)
AfterDate(selector, date) IsAfter(selector, date)
CountEquals(selector, n) Count(selector, n)

See CHANGELOG.md for the full version history.

Performance Tips ⚑

Choose the right method for your use case:

Method Best for Compilation
IsValid(item) Ad-hoc single checks Compiles full predicate on first call, caches it
BuildCached() Batch filtering of large collections Compiles once β€” reuse the returned Func<T, bool>
Validate(item) Error collection (field-level details) Each condition compiled independently (lazy per entry)
ValidateAll(items) Per-item error details on a list Calls Validate() per item β€” prefer when you need error context

For filtering large collections, always prefer BuildCached() over Validate():

// βœ… Fast β€” expression compiled once, cached delegate reused
var isValid = validator.BuildCached();
var validItems = records.Where(isValid).ToList();

// ⚠️ Slower β€” Validate() compiles lazily per condition on each call
var validItems = records.Where(r => validator.Validate(r).IsValid).ToList();

When you need structured error output for a batch:

// Use ValidateAll() β€” freezes the builder once, iterates Validate() per item
var results = validator.ValidateAll(records)
    .Where(r => !r.Result.IsValid)
    .ToList();

Donations πŸ’–

If you find Vali-Flow.Core useful and would like to support its development, consider making a donation:

Your contributions help keep this project alive and improve its development! πŸš€

Changelog πŸ“‹

See CHANGELOG.md for a detailed history of all changes.

License πŸ“œ

MIT Β© 2026 Felipe Rafael Montenegro Morriberon

Contributions 🀝

Contributions are welcome! Feel free to open issues and submit pull requests to improve this library.

  • Bug reports β€” open an issue with a minimal reproducible example
  • Feature requests β€” open an issue describing the use case
  • Pull requests β€” fork the repo, create a branch, and submit a PR against main

See the GitHub repository to get started.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  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.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages (7)

Showing the top 5 NuGet packages that depend on Vali-Flow.Core:

Package Downloads
Vali-Flow

Vali-Flow is a .NET library that simplifies data access in Entity Framework Core applications via a fluent specification API. Provides ValiFlowEvaluator for querying and writing data, QuerySpecification and BasicSpecification for defining query criteria, and bulk operation support via EFCore.BulkExtensions.

Vali-Flow.InMemory

Synchronous in-memory evaluator for Vali-Flow.Core β€” filter, page, aggregate and write in-memory collections using the same fluent ValiFlow<T> specification API. Ideal for unit testing and caching layers without a database dependency.

Vali-Flow.Abstractions

Provider-agnostic contracts for the Vali-Flow ecosystem. Contains shared interfaces (IQueryEvaluator, IExpressionTranslator) and utilities (ExpressionInspector) used by all Vali-Flow adapters (EF Core, In-Memory, SQL, and future providers).

Vali-Flow.NoSql

Provider-agnostic NoSql IR for Vali-Flow.Core β€” converts expression trees to a neutral condition node tree that can be translated to MongoDB, Elasticsearch, or any document store.

Vali-Flow.Sql

SQL translator for Vali-Flow.Core β€” converts ValiFlow<T> expression trees to parameterized SQL for Dapper and ADO.NET. Supports SQL Server, PostgreSQL, MySQL, and SQLite dialects.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.2 157 4/15/2026
2.0.1 112 4/12/2026
2.0.0 587 4/12/2026
1.0.4 280 4/24/2025
1.0.3 235 4/8/2025
1.0.2 173 3/14/2025
1.0.1 261 3/14/2025 1.0.1 is deprecated because it is no longer maintained and has critical bugs.

## 2.0.1
### Documentation & Metadata
- README.md: removed inline logo, improved License and Contributions sections
- PackageProjectUrl: now points to https://vali-flow-docs.netlify.app/docs/core/getting-started (was GitHub URL)
- PackageRequireLicenseAcceptance: set to true (aligns with license visibility)
- New companion packages: Vali-Flow.Core.Analyzers 1.0.0 and Vali-Flow.Core.Generator 1.0.0 now require Vali-Flow.Core >= 2.0.0
- TargetFrameworks: Analyzers & Generator now target net8.0;net9.0 (was netstandard2.0) for Vali-Flow.Core compatibility

## 2.0.0
### Architecture
- Source generator (Vali-Flow.Core.Generator): eliminates ~468 manually-maintained delegation methods. ValiFlow and ValiFlowQuery reduced to ~110 lines each β€” thin partial facades.
- 9 query-specific interfaces added (IBooleanExpressionQuery, IStringExpressionQuery, ICollectionExpressionQuery, INumericExpressionQuery, IDateTimeExpressionQuery, IDateTimeOffsetExpressionQuery, IDateOnlyExpressionQuery, ITimeOnlyExpressionQuery).
- BaseExpression.ValidateNested: extracted protected virtual CreateNestedBuilder factory method β€” decouples base class from ValiFlow concrete type.

### Infrastructure
- Vali-Flow.Core.Analyzers: Roslyn analyzer VFCORE001 β€” warns when non-EF Core methods are used on ValiFlowQuery inside IQueryable contexts.

### Breaking Changes
- Removed deprecated methods: BeforeDate, AfterDate (use IsBefore/IsAfter), CountEquals (use Count).
- IStringExpression now inherits from 4 focused sub-interfaces: IStringLengthExpression, IStringContentExpression, IStringStateExpression, IStringFormatExpression. If you implemented IStringExpression directly, implement the 4 sub-interfaces instead.

### Performance
- ConditionEntry compiles predicates lazily via Lazy β€” thread-safe without explicit locking. Validate() no longer acquires a lock per condition.

## 1.7.0
### Architecture
- ValiFlowQuery God Class eliminated: reduced from 2329 lines to ~530 lines by extracting all method bodies into 9 domain-specific composition classes (BooleanExpressionQuery, StringExpressionQuery, CollectionExpressionQuery, NumericExpressionQuery, DateTimeExpressionQuery, DateTimeOffsetExpressionQuery, DateOnlyExpressionQuery, TimeOnlyExpressionQuery). Public API unchanged.

### New Features β€” ValiFlowQuery (EF Core-safe)
- String: IsTrimmed, IsLowerCase, IsUpperCase, EqualToIgnoreCase, StartsWithIgnoreCase, EndsWithIgnoreCase, ContainsIgnoreCase, NotContains, NotStartsWith, NotEndsWith
- Numeric: IsEven, IsOdd, IsMultipleOf(n) (int and long)
- DateTime/DateTimeOffset/DateOnly: IsWeekend, IsWeekday, IsDayOfWeek, IsToday, IsYesterday, IsTomorrow, InLastDays, InNextDays, IsFirstDayOfMonth, IsLastDayOfMonth, IsInQuarter

### Test Coverage
- 961 tests (net9.0) β€” full coverage of ValiFlow, ValiFlowQuery, BaseExpression, all type-specific classes, ValiFlowGlobal, ValiSort
- `ValiFlowQuery.FutureDate/PastDate` (DateTimeOffset): corrected EF Core provider remarks β€” `DateTimeOffset.UtcNow` is not guaranteed to translate on MySQL (Pomelo) or SQLite

### Documentation
- ValiFlow<T>: added class-level summary and example β€” was the only public entry-point type without any XML doc
- ValiFlowQuery<T>: removed SQLite from the "all major providers" blanket claim; added explicit SQLite .DayOfWeek note
- `Freeze()` and `BuildCached()` on `IExpression`: updated to describe fork semantics instead of IOE-throw semantics

### Test Coverage
- `CollectionExpressionNegativeTests`: 14 new tests β€” null predicate guards for All/Any/None/EachItem/AnyItem, empty collection edge cases for HasDuplicates/DistinctCount, CountBetween boundary tests
- `RegressionTests`: 3 new tests β€” Validate with no annotations, mixed annotated/unannotated, ValidateAll with empty sequence
- `ValiFlowGlobalTests`: 4 new tests β€” Register/BuildWithGlobal, ClearAll state isolation, no-globals parity with Build, multiple globals AND semantics
- Total: 812 tests

## 1.7.0
### Breaking change β€” Freeze semantics (mutation no longer throws)
- Freeze pattern redesigned: mutation methods called on a frozen builder now return a new independent
 clone (fork) instead of throwing `InvalidOperationException`. This is identical to how
 `IQueryable.Where()` always produces a new query object without modifying the original.
 The breaking change only affects code that relied on `IOE` being thrown after freeze β€” the
 correct action is simply not to assign the result, which was already accepted by the compiler.

### Performance
- `Clone()` is now O(1) via `ImmutableList<T>` structural sharing. The condition list is stored
 as an `ImmutableList` internally; cloning assigns the reference without copying elements.
 Previously O(n) foreach loop.

### New behavior
- Calling any mutation method on a frozen builder implicitly returns a derived fork:
 `var specialized = sharedFrozenBuilder.GreaterThan(x => x.Age, 18)` β€” no explicit `.Clone()` needed.
- Explicit `.Clone()` is still available for clarity and is equivalent to the implicit fork.

### Test coverage
- Updated freeze tests to assert fork behavior (5 tests)
- Added `Clone_ImplicitFork_FrozenBuilderMutationReturnsDerivedBuilder` test
- Total: 791 tests

## 1.6.1
### New Features
- `Clone()`: creates a new independent builder pre-populated with all conditions from the source.
 Enables IQueryable-style derivation: build a base set of rules, then create specializations.
- `Freeze()` method now exposed on `IExpression<TBuilder, T>` interface.

### Test coverage
- Added `CloneTests` class: 3 tests β€” base conditions copied, multiple independent derivations, clone of frozen starts unfrozen
- Total: 790 tests

## 1.6.0
### New Features
- **Freeze pattern**: builders are now permanently sealed on first call to `BuildCached()`, `IsValid()`,
 `IsNotValid()`, `Validate()`, or explicit `Freeze()`. After sealing, any mutation attempt throws
 `InvalidOperationException`. This eliminates the thread-safety gap where `List<T>` mutation
 after first use could cause silent corruption in concurrent scenarios.
- Exposed `Freeze()` on `IExpression<TBuilder, T>` interface.

### Test coverage
- Added 6 freeze tests in `BuildCachedTests`
- Updated 2 pre-existing `BuildCached` cache-invalidation tests to reflect new freeze semantics
- Total: 790 tests

## 1.5.0
### Documentation
- ValiFlowQuery: added XML <summary> and EF Core <remarks> to all previously undocumented numeric methods (long/double/decimal/float/short scalar overloads β€” Zero/NotZero/Positive/Negative/GreaterThan/GreaterThanOrEqualTo/LessThan/LessThanOrEqualTo/MinValue/MaxValue/InRange scalar and cross-property)
- ValiFlowQuery: added XML docs to int InRange scalar and cross-property overloads
- ValiFlowQuery: added XML docs to all DateTime methods β€” IsBefore/IsAfter/SameMonthAs/SameYearAs/IsInMonth/IsInYear
- ValiFlowQuery: added XML docs to all DateTimeOffset methods β€” IsBefore/IsAfter/BetweenDates (scalar)
- ValiFlowQuery: added XML docs to all DateOnly methods β€” IsBefore/IsAfter/BetweenDates/IsInMonth/IsInYear
- ValiFlowQuery: added XML docs to all TimeOnly methods β€” IsBefore/IsAfter/IsAM/IsPM/IsExactTime/IsInHour

## 1.4.9
### Bug Fixes
- NumericExpression all nullable overloads (IsNullOrZero/HasValue/GreaterThan/LessThan for int?/decimal?/long?/double?): added missing selector null guard β€” completes null-guard parity with float? overloads
- NumericExpression nullable InRange (int?/decimal?/long?): selector null guard now runs before max < min validation β€” eliminates wrong-exception-type scenario

### New Features
- NumericExpression/ValiFlow/INumericExpression: added full nullable comparison overloads for double?, float?, and short? β€” GreaterThan/LessThan/InRange; IsNullOrZero(short?); HasValue(short?) β€” completes API parity across all numeric types
- ValiFlowQuery: added FutureDate(DateTimeOffset) and PastDate(DateTimeOffset) β€” EF Core translatable via GETUTCDATE()/UTC_TIMESTAMP()

### Test Coverage
- Added 2 new tests: FutureDate and PastDate for DateTimeOffset on ValiFlowQuery
- Total: 782 tests

## 1.4.8
### Bug Fixes
- NumericExpression scalar InRange (int/long/float/double/decimal/short): added selector null guard BEFORE max < min validation β€” eliminates wrong-exception-type scenario when selector is null and max < min
- NumericExpression all scalar overloads (Zero/NotZero/GreaterThan/GreaterThanOrEqualTo/LessThan/LessThanOrEqualTo/Positive/Negative/MinValue/MaxValue/IsEven/IsOdd/IsMultipleOf): added selector null guard at method boundary β€” consistent API contract across all 50 overloads
- ValiFlowQuery StartsWith/EndsWith/Contains: added selector null guard before value validation β€” eliminates wrong exception when selector is null and value is also invalid

### New Features
- ValiFlowQuery: added CountBetween<TValue> β€” validates collection element count is within [min, max]; completes API parity with CollectionExpression

### Test Coverage
- Added 4 new tests: CountBetween within range, CountBetween null collection, InRange null-selector ordering (regression)
- Total: 780 tests

## 1.4.7
### Bug Fixes
- NumericExpression InRange<TValue>(IComparable): added missing ArgumentNullException selector null guard β€” completes null-guard coverage for all IComparable overloads
- DateTimeExpression BetweenDates cross-property: replaced fragile Convert fallback with CloneExpression helper that handles MemberExpression, UnaryExpression, and MethodCallExpression β€” eliminates malformed node for non-MemberExpression selectors

### New Features
- ValiFlowQuery: added nullable float? overloads β€” GreaterThan, LessThan, InRange β€” completes API parity with double? section
- ValiFlowQuery: added nullable short? overloads β€” IsNullOrZero, HasValue, GreaterThan, LessThan, InRange β€” completes full nullable coverage for all numeric types

### Test Coverage
- Added 9 new tests: float? GreaterThan/LessThan/InRange on ValiFlowQuery, short? IsNullOrZero/HasValue/GreaterThan/LessThan/InRange on ValiFlowQuery
- Total: 778 tests

## 1.4.6
### Bug Fixes
- ValiFlowQuery: removed IsWeekend/IsWeekday/IsDayOfWeek for DateTime/DateTimeOffset/DateOnly β€” DayOfWeek is not EF Core translatable on SQL Server, MySQL, or Oracle; violates EF-safe builder contract (methods remain available on ValiFlow<T>)
- NumericExpression cross-property InRange: valBody now cloned for LessThanOrEqual node β€” eliminates DAG aliasing in all 6 overloads
- DateTimeExpression/DateOnlyExpression BetweenDates cross-property: valBody cloned for second comparison node
- ValiFlowQuery BetweenDates cross-property (DateTime/DateOnly): valBody cloned via ForceCloneVisitor
- ValiFlowQuery nullable InRange (int?/long?/decimal?): added missing ArgumentNullException selector null guard

### New Features
- ValiFlowQuery: added cross-property InRange overloads for long, double, float, and short β€” completes API parity with ValiFlow<T>

### Test Coverage
- Added 7 new tests: cross-property InRange for long/double/float/short on ValiFlowQuery, BetweenDates cross-property DateOnly on ValiFlow
- Removed 6 tests referencing removed DayOfWeek methods on ValiFlowQuery
- Total: 757 tests

## 1.4.5
### Bug Fixes
- ValiFlowQuery ValidateNested: added ForceCloneVisitor for null-check node β€” eliminates expression tree node aliasing (matching BaseExpression fix)
- NumericExpression cross-property IComparable<TValue>: added CloneSelectorBody helper β€” both selectorBodyForCall and freshBody now use reference-distinct nodes in all 4 overloads
- StringExpression ExpressionDeepCloner.VisitMethodCall: added explicit null guard for static method calls (node.Object == null), aligning with CollectionExpression pattern
- ValiFlowQuery MaxCount: null collection now returns true (was false) β€” aligns with CollectionExpression.MaxCount semantics (null = 0 items ≀ max)

### Test Coverage
- Added 20 new tests: IsDayOfWeek(DateTimeOffset) on ValiFlowQuery, nullable long?/decimal? overloads (IsNullOrZero/HasValue/GreaterThan/LessThan), TimeOnly IsAfter/IsPM/IsExactTime on ValiFlowQuery, MaxCount null regression, cross-property InRange for long/double/float/short on ValiFlow, BetweenDates cross-property DateTime on ValiFlow, HasValue(float?) and IsNullOrZero(float?) on ValiFlow
- Total: 759 tests

## 1.4.4
### New Features
- NumericExpression/ValiFlow/ValiFlowQuery: added HasValue(float?) overload β€” completes the nullable numeric HasValue set

### Bug Fixes
- BaseExpression IsNotValid: replaced plain field read post-CompareExchange with atomic return β€” eliminates stale-delegate race on ARM64
- BaseExpression ValidateNested: replaced no-op ParameterReplacer with ForceCloneVisitor to produce reference-distinct null-check node (real fix for expression tree node aliasing)
- CollectionExpression All/Any/None/EachItem/AnyItem: null-check now uses its own clone of selectorBody β€” eliminates node aliasing when the same selector is used in multiple calls
- NumericExpression cross-property IComparable<TValue>: else-branch builds selectorBodyForCall as a fresh node so selectorBody is no longer aliased between callExpr and nullCheck
- StringExpression RegexMatch: cache-full guard now uses Volatile.Read β€” prevents stale read bypassing the cap on ARM64
- DateTimeOffsetExpression InLastDays/InNextDays: aligned guard to days <= 0 (was days < 0), matching DateTimeExpression contract
- ValiFlowQuery: added ArgumentNullException selector null guard to all string, collection, numeric scalar, DateOnly, and TimeOnly methods

### Test Coverage
- Added 11 new tests: cross-property InRange (ValiFlowQuery int/decimal, ValiFlow int + alias regression), IsDayOfWeek (DateTime/DateOnly), BetweenDates cross-property (DateTime/DateOnly), HasValue(double?/float?/int?)
- Total: 741 tests

## 1.4.3
### Bug Fixes
- BaseExpression BuildCached: replaced plain field read after CompareExchange with atomic return β€” eliminates stale-read risk on ARM64
- BaseExpression ValidateNested: cloned selector.Body for null-check node to eliminate expression tree node aliasing
- NumericExpression cross-property IComparable<TValue>: fixed node aliasing in all 4 overloads (GreaterThan/GreaterThanOrEqualTo/LessThan/LessThanOrEqualTo) for reference-type TValue
- DateTimeOffsetExpression InLastDays/InNextDays: date now evaluated inside lambda (was captured at builder-construction time, causing stale results when predicate was reused across a day boundary)
- CollectionExpression ExpressionDeepCloner.VisitMethodCall: guard against null Object (static method calls) to prevent NullReferenceException
- StringExpression RegexMatch: Interlocked.Increment now fires only on successful TryAdd β€” eliminates overcount that caused premature cache-full exception under concurrency
- RegularExpression PhonePattern: + prefix now mandatory (was optional), enforcing strict E.164 format
- ValiFlowQuery IsTrue: added ArgumentNullException null guard on selector (was the only method in the class missing it)
- ValiFlowQuery nullable numeric overloads (IsNullOrZero/HasValue/GreaterThan/LessThan for int?/long?/decimal?/double?): added ArgumentNullException null guard on selector
- ValiFlowQuery IsLastDayOfMonth(DateOnly): removed from EF-safe builder β€” uses DateTime.DaysInMonth which is not EF Core translatable (violates class contract); remains available on ValiFlow<T>

### Test Coverage
- Added 25 new tests: IsNullOrZero(float?), double? GreaterThan/LessThan/InRange, BeforeDate/AfterDate/ExactDate (DateTime), SameMonthAs/SameYearAs, IsInMonth(DateTime), IsWeekday(DateTime/DateOnly), IsTrue null guard, IsEven/IsOdd/IsMultipleOf for long
- Total: 730 tests

## 1.4.2
### New Features
- NumericExpression/ValiFlowQuery: added IsNullOrZero(float?) overload for nullable float columns

### Bug Fixes
- NumericExpression EqualTo<TValue>: added ArgumentNullException null guard with "Use Null() to check for null values." message
- ValiFlowQuery IsInMonth/IsInYear (DateTimeOffset): added EF Core UTC-vs-offset divergence warning in XML docs

## 1.4.1
### Bug Fixes
- StringExpression RegexMatch: cache cap now enforced with atomic Interlocked counter β€” thread-safe under high concurrency
- ValiFlowQuery DateTime/DateTimeOffset: added ArgumentNullException null guard on selector in all 22 delegating methods
- ValiFlowQuery IsNullOrWhiteSpace/IsNotNullOrWhiteSpace: corrected EF Core remarks β€” explicitly documents Pomelo < 5.0 as unsupported and recommends IsNullOrEmpty as fallback

## 1.4.0
### New Features
- ValiFlowQuery: added GreaterThan(double?), LessThan(double?), InRange(double?) overloads for nullable double columns
- ValiFlowQuery: added IsLastDayOfMonth(DateOnly) with EF Core note
- ValiFlowQuery: added BeforeDate/AfterDate for DateTime now normalize to date.Date (consistent semantics with ValiFlow)

### Bug Fixes
- StringExpression: RegexMatch cache now capped at 1,000 entries to prevent unbounded memory growth
- CollectionExpression All/Any/None/EachItem/AnyItem: fixed shared selector.Body node across two tree positions (ExpressionDeepCloner)
- CollectionExpression DistinctCount: added count < 0 guard
- CollectionExpression MaxCount: null collection now correctly returns true (has ≀ max items)
- CollectionExpression Contains<TValue>: added null guard on selector
- NumericExpression: generic scalar comparison overloads now guard against null value argument
- ValiFlowQuery Contains: rejects empty string (would match every non-null row)
- ValiFlowQuery IsWeekend/IsWeekday/IsDayOfWeek: added EF Core untranslatability warning (DayOfWeek not supported on SQL Server/MySQL/Oracle)
- RegularExpression: E.164 phone pattern minimum raised to 7 digits (was 2)
- RegularExpression: email pattern now rejects leading/trailing dots and hyphen-leading domain labels
- StringExpression Contains multi-selector: splits on all whitespace (was space-only)
- DateTimeOffsetExpression InLastDays/InNextDays: UtcNow captured once per call (was read twice causing boundary race)
- DateTimeExpression/DateTimeOffsetExpression/DateOnly/TimeOnly/Boolean: null guards added to all single-selector methods
- ValiFlowQuery nullable numeric section: added EF Core remarks confirming Nullable.HasValue/Value translation

### Documentation
- ValiFlowQuery class-level doc: scoped EF Core safety guarantee to major providers (SQL Server, PostgreSQL, MySQL Pomelo 5.0+, SQLite, Oracle 7.0+)
- SameMonthAs/SameYearAs: added XML docs confirming EF Core translatability
- IsJson: documented that bare primitives (42, true, null) are accepted
- BuildWithGlobal: added EF Core safety warning about globally registered filters
- ValidateAll: added Or-grouping blind spot warning

### Test Coverage
- Added 41 new tests covering: RegexMatch, HasLettersAndNumbers, HasSpecialCharacters, IsPhoneNumber, NotJson, NotBase64, IsToday/IsYesterday/IsTomorrow/IsLeapYear/InLastDays/InNextDays/BeforeDate/AfterDate/ExactDate (DateTime), numeric type overloads (long/decimal/double/short), null edge cases for string methods
- Total: 711 tests

## 1.3.0
### New Features
- DateTimeOffsetExpression: added IsYesterday and IsTomorrow (in-memory only)

### Bug Fixes
- Combine() / & / | operators: short-circuit when either builder is empty (fixes EF Core Constant(true) translation error)
- IComparable<TValue> cross-property overloads (GreaterThan/GreaterThanOrEqualTo/LessThan/LessThanOrEqualTo): null guard now skipped for value types, preventing ArgumentException on DateTime/int/decimal
- IComparable<TValue> scalar overloads: null guard prevents NullReferenceException when value is null reference type
- AddSubGroup empty action: throws ArgumentException with clear message mentioning sub-group instead of "always true"
- ValidateNested empty configure: throws ArgumentException instead of silently producing a null-only check
- ComparisonExpression EqualTo/NotEqualTo: throws ArgumentNullException with "Use Null() to check for null values." message
- Base64Pattern regex: enforces groups-of-4 (length must be multiple of 4 with correct padding)
- UrlPattern regex: removed unescaped dot that matched whitespace characters
- EqualsIgnoreCase: corrected XML doc (not EF Core translatable)

### Test Coverage
- Added ValiFlowQueryTests: 65 tests covering all ValiFlowQuery<T> method groups
- Added regression tests for all bug fixes in this release
- Added IsYesterday/IsTomorrow tests for DateTimeOffset
- Total: 665 tests

## 1.2.0
### New Features
- ValiFlowQuery<T>: new EF Core-safe builder that only exposes methods translatable to SQL β€” Boolean, Comparison, String (11 methods), Collection (7), Numeric (typed overloads for int/long/double/decimal/float/short + nullable), DateTime (14), DateTimeOffset (8), DateOnly (9), TimeOnly (7)
- Excludes all in-memory-only methods: RegexMatch, IsEmail, IsUrl, IsPhoneNumber, IsGuid, IsJson, IsBase64, IsEven, IsOdd, IsMultipleOf, IsToday, IsYesterday, IsTomorrow, InLastDays, InNextDays, IsLeapYear, IsLastDayOfMonth, All, Any, None, AnyItem, EachItem, HasDuplicates, DistinctCount, IComparable<T> generic overloads
- Static operators &, |, ! and Combine() for composing ValiFlowQuery<T> expressions

## 1.1.0
### New Features
- ValidateNested<TProperty>: validate nested objects with automatic null check
- AnyItem<TValue>: pass if at least one collection element satisfies conditions
- EachItem<TValue>: pass if all collection elements satisfy conditions
- BuildCached(): compile expression once and reuse (thread-safe)
- BuildWithGlobal(): combine local conditions with ambient ValiFlowGlobal filters
- ValiSort<T>: fluent sort builder with By/ThenBy, applies to IQueryable and IEnumerable
- ValiFlowGlobal: static ambient filter registry (thread-safe)
- When/Unless: conditional condition blocks evaluated at runtime
- AddIf: conditionally add conditions based on a bool flag
- WithSeverity / Severity enum: mark errors as Info, Warning, Error, or Critical
- ValidationResult: Warnings, CriticalErrors, ErrorsAtOrAbove(), HasAnySeverity()
- PropertyPath on ValidationError: identify which property failed
- ValidateAll: batch validation returning per-item results
- Explain(): human-readable description of the built expression tree
- DateTimeOffset, DateOnly, TimeOnly expression support
- IComparable<T> generic comparison overloads (in-memory)
- Cross-property InRange comparisons (EF Core compatible)
- ExpressionExplainer utility

### Bug Fixes
- Fixed Or() state machine: _groupConditions removed, flat list with isAnd flag
- Fixed BuildCached() thread safety: Volatile.Read + Interlocked.CompareExchange
- Fixed IsValid/IsNotValid recompilation on every call
- Fixed NotEmpty null guard
- Fixed Contains(string, List<selectors>) OR-logic composition
- Fixed ValidateExpressionBody false-positive for binary-zero expressions
- Fixed ArgumentOutOfRangeException constructors in InLastDays/InNextDays
- Fixed RegexMatch: Regex compiled once at build time
- Fixed string.Contains(StringComparison): EF Core compatible via ToLower()
- Fixed cross-property InRange: Expression.Invoke replaced with ParameterReplacer
- Fixed All/Any/None/EachItem/AnyItem: Expression.Quote for EF Core compatibility
- Fixed Validate() thread safety: lock on lazy compile write-back
- Fixed DateTimeOffset/DateOnly stale date capture in IsToday/IsFuture/IsPast
- Fixed CountBetween: null guard and min/max validation
- Fixed InRange: min <= max validation
- Fixed ValiSort enumerable reflection: MethodInfo cached per key type (plain Dictionary, non-thread-safe by design)