Compare commits

..
Author SHA1 Message Date
ae2c2679f2
feat(dispatcher): add registration-time pipeline behavior ordering via AddBehavior<T>(order:)
All checks were successful
default / dotnet-default-workflow (push) Successful in 1m58s
Pipeline behavior ordering previously depended on discovery/registration
order. Add an AddBehavior<T>(order:) API that captures the order at
registration time; the BehaviorTypeIndex now orders matching behaviors by
that explicit order (lower runs first), falling back to registration order
for ties. Removed the runtime Order property approach.
2026-07-12 21:25:22 +02:00
595ba01ea6
fix(dispatcher): resolve handlers in registration order across closed and open generics
Handler/behavior resolution previously forced closed generics before open
generics regardless of registration order, which was surprising and fragile.
TypeIndex now tracks each registered type's index and orders matching
candidates by registration order, so closed/open ordering is deterministic
and follows registration.
2026-07-12 21:25:21 +02:00
f79e8780f7
fix(dispatcher): throw on ambiguous handlers instead of silent .First()
Multiple handlers for the same request were previously resolved via .First(),
with ordering depending on Type.GetInterfaces() order (non-deterministic).
Now ScalarRequestInvoker/StreamRequestInvoker throw InvalidOperationException
listing the candidate handlers when more than one matches.
2026-07-12 21:25:21 +02:00
f7263c33a8
fix(validation): make Severity meaningful in Validation.IsValid
Validation.IsValid previously counted every problem regardless of severity,
leaving Severity inert. Now IsValid only fails on Error problems, so Warning
and Info no longer invalidate the result, and IsValidFor(Severity) lets
callers set the failing-severity threshold.
2026-07-12 21:25:21 +02:00
552f88df18
fix(validation): return no problems for null reference instance in Rule.Validate
Rule<T,TProperty>.Validate cast a null instance to T and validated it, so a
reference T whose accessor dereferences the instance threw
NullReferenceException on a direct validator.Validate(context) with a null
instance. Guard the null-instance path and return no problems instead.
2026-07-12 21:25:21 +02:00
68194d3cb3
fix(validation): guard null comparison bounds in comparison builders
Passing a null bound to GreaterThan/LessThan/Between etc. called
value.CompareTo(null), throwing NullReferenceException for non-null
reference-type values. Short-circuit when the bound is null so the rule is
satisfied instead of crashing.
2026-07-12 21:25:21 +02:00
ab9407085d
docs(result): document that failure equality ignores error content
Result/Result<T> equality treats any two failures as equal regardless of their
Error, which was undocumented and surprising for failure-specific branching.
2026-07-12 21:25:21 +02:00

View file

@ -45,7 +45,7 @@ To have a consistent experience across all packages, some public interfaces have
### Changed ### Changed
- **request.result:** Document that `Result`/`Result<T>` equality treats any two failures as equal, ignoring error content; branch on the `Error` directly for failure-specific logic - **request.result:** Document that `Result`/`Result<T>` equality treats any two failures as equal, ignoring error content; branch on the `Error` directly for failure-specific logic (see PLAN.md Inconsistencies#E)
### Fixed ### Fixed
@ -53,10 +53,10 @@ To have a consistent experience across all packages, some public interfaces have
- **request.result:** Fold `IsSuccess` into `Result<T>.GetHashCode` to avoid collisions between a failure and a success value whose hash is `0` - **request.result:** Fold `IsSuccess` into `Result<T>.GetHashCode` to avoid collisions between a failure and a success value whose hash is `0`
- **request.validation:** Guard null comparison bounds in `GreaterThan`/`LessThan`/`Between` etc. so a `null` bound no longer throws (`NullReferenceException`) - **request.validation:** Guard null comparison bounds in `GreaterThan`/`LessThan`/`Between` etc. so a `null` bound no longer throws (`NullReferenceException`)
- **request.validation:** Return no problems when validating a `null` instance of a reference type instead of dereferencing it (`NullReferenceException`) - **request.validation:** Return no problems when validating a `null` instance of a reference type instead of dereferencing it (`NullReferenceException`)
- **request.validation:** Make `Severity` meaningful — `Validation.IsValid` now only fails on `Error` problems; `Warning`/`Info` no longer invalidate. Added `Validation.IsValidFor(Severity)` to set the failing-severity threshold - **request.validation:** Make `Severity` meaningful — `Validation.IsValid` now only fails on `Error` problems; `Warning`/`Info` no longer invalidate. Added `Validation.IsValidFor(Severity)` to set the failing-severity threshold (see PLAN.md Inconsistencies#B)
- **request.dispatcher:** Throw `InvalidOperationException` listing candidates when multiple handlers match a request instead of silently resolving via `.First()`. Also throws when no handler is registered - **request.dispatcher:** Throw `InvalidOperationException` listing candidates when multiple handlers match a request instead of silently resolving via `.First()` (matches MediatR). Also throws when no handler is registered (see PLAN.md Inconsistencies#C)
- **request.dispatcher:** Resolve handlers/behaviors in registration order across closed and open generics instead of the fixed closed-before-open reflection order - **request.dispatcher:** Resolve handlers/behaviors in registration order across closed and open generics instead of the fixed closed-before-open reflection order (see PLAN.md Inconsistencies#D)
- **request.dispatcher:** Add `AddBehavior<T>(order:)` registration-time API so pipeline behavior ordering is explicit and deterministic (lower order runs first, registration order as tie-break) instead of relying on reflection/discovery order - **request.dispatcher:** Add `AddBehavior<T>(order:)` registration-time API so pipeline behavior ordering is explicit and deterministic (lower order runs first, registration order as tie-break) instead of relying on reflection/discovery order (see PLAN.md Tips — Explicit pipeline ordering API)
### Removed ### Removed