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
<PackageReference Include="Vali-Flow.Core" Version="2.0.2" />
<PackageVersion Include="Vali-Flow.Core" Version="2.0.2" />
<PackageReference Include="Vali-Flow.Core" />
paket add Vali-Flow.Core --version 2.0.2
#r "nuget: Vali-Flow.Core, 2.0.2"
#:package Vali-Flow.Core@2.0.2
#addin nuget:?package=Vali-Flow.Core&version=2.0.2
#tool nuget:?package=Vali-Flow.Core&version=2.0.2
Vali-Flow.Core - Fluent Expression Builder for .NET Validation
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 anIReadOnlyList<ValidationError>backed byAsReadOnly()β casting toList<T>will fail at runtime.
Performance
Validate()short-circuits OR groups β returnsOk()as soon as any group passes, without evaluating remaining groups.ValidationResult.WarningsandCriticalErrorsare now lazy β computed only when accessed, not on construction.- Regex cache in
RegexMatchis now global (shared across all types) instead of per-closed-generic-type. BuildWithGlobal()cachesBuild()β multiple calls on a frozen builder reuse the same expression tree.
API Improvements
CreateNestedBuilder<TProperty>()is nowvirtualβ external subclasses ofBaseExpressionno longer required to override it.Severity.Infobehavior clarified: only appears inValidationResultwhen the condition fails AND has an attached message.WithMessage(Func<string>)factory must not returnnullβ documented in IntelliSense.- All regex methods (
IsEmail,IsUrl,RegexMatch, etc.) now documentRegexMatchTimeoutExceptionin 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β useIsBefore/IsAfter(full DateTime/DateTimeOffset comparison including time-of-day). - Removed
CountEqualsβ useCount(identical semantics). IStringExpression<TBuilder,T>segregated into 4 focused sub-interfaces:IStringLengthExpressionβ MinLength, MaxLength, ExactLength, LengthBetweenIStringContentExpressionβ StartsWith, EndsWith, Contains, EqualToIgnoreCase, IsOneOfIStringStateExpressionβ IsNullOrEmpty, IsNullOrWhiteSpace, IsTrimmed, IsLowerCase, IsUpperCase, HasOnlyDigits/Letters/SpecialCharactersIStringFormatExpressionβ IsEmail, IsUrl, IsGuid, IsJson, IsBase64, RegexMatch, MatchesWildcard, IsCreditCard, IsIPv4/IPv6, IsHexColor, IsSlug- If you implemented
IStringExpressiondirectly, implement the 4 sub-interfaces instead.
Performance
ConditionEntry<T>now compiles predicates lazily viaLazy<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:
- For Latin America: Donate via MercadoPago
- For International Donations: Donate via PayPal
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 | Versions 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. |
-
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.
## 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)