Conversation
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
…idators Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
… critera Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
…ation Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
…ehaves like a value object Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
…zer should be used Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
There was a problem hiding this comment.
Pull request overview
This PR introduces a new Light.PortableResults.Validation project that provides framework-agnostic validation foundations for DTO-centric workflows. The design uses scoped ValidationContext structs over shared ValidationState, immutable configuration via ValidationContextOptions, policy-based string normalization and null-error handling, and rich message templates. Public validators return Result<T> while using ValidatedValue<T> internally for normalized/transformed values.
Changes:
- New
Light.PortableResults.Validationproject (netstandard2.0) with validation contexts, checks, sync/async validator base classes, target normalization, error templates, and configuration types. - New
Light.PortableResults.Validation.Testsproject with comprehensive unit tests covering validation semantics, context optimization, configuration, target normalization, and validated values. - Benchmark suite comparing against FluentValidation 12.1.1, plus focused microbenchmarks for context operations, error accumulation, and configuration.
Reviewed changes
Copilot reviewed 66 out of 67 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/Light.PortableResults.Validation/*.cs |
Core validation types: ValidationContext (struct), ValidationState, Check<T>, Validator base classes, target normalizer, options, templates, error messages |
src/Light.PortableResults.Validation/*.csproj |
Project targeting netstandard2.0 with polyfill packages |
tests/Light.PortableResults.Validation.Tests/*.cs |
Unit tests for validators, context operations, configuration, normalization, and ValidatedValue |
benchmarks/Benchmarks/Validation*.cs |
Endpoint, context, and configuration benchmarks vs FluentValidation |
benchmarks/Benchmarks/Benchmarks.csproj |
Added FluentValidation, Validation, and MinimalApis references |
ai-plans/0024-*.md |
Design plans and deviation documentation |
src/AGENTS.md, tests/AGENTS.md, AGENTS.md |
Updated documentation for new project |
Light.PortableResults.slnx |
Solution file updated with new projects |
Directory.Packages.props |
Added FluentValidation package version |
BenchmarkDotNet.Artifacts/results/* |
Benchmark result artifacts |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
…tring in ValidationContext.NormalizeValueIfNecessary Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
Minimum allowed line rate is |
0024 Plan Deviations
This document compares
ai-plans/0024-validation-support.mdwith the current implementation state on this branch.It also tracks the follow-up changes introduced through:
ai-plans/0024-validation-context-optimization.mdai-plans/0024-options-and-error-templates-optimization.mdai-plans/0024-validation-outcome-removal.mdai-plans/0024-normalization-optimization.mdDeviations From The Original Plan
1. Public validator APIs now return
Result<T>instead ofValidationOutcome<T>Reference:
0024-validation-outcome-removal.mdOriginal plan:
The original design centered the validation package on a dedicated public result model named
ValidationOutcome<T>.Synchronous validators were supposed to return
ValidationOutcome<T>/ValidationOutcome<TValidated>, asynchronousvalidators were supposed to return
ValueTask<ValidationOutcome<T>>/ValueTask<ValidationOutcome<TValidated>>,and failing outcomes could still expose the best available validated value.
Implemented:
The public validator contract was aligned with the core library and now returns
Result<T>/Result<TValidated>andValueTask<Result<T>>/ValueTask<Result<TValidated>>.ValidationOutcome<T>was removed from the public API and replaced withValidatedValue<T>, a success-only carrierused on protected and internal validator execution paths. Validation errors now live exclusively in
ValidationContext, and failed public results no longer expose normalized or transformed values.The endpoint-oriented convenience methods from the original plan still exist, but they now sit on top of the
Result<T>contract:CheckForErrors(...)materializes a non-genericResultonly on failure.TryValidate(...)returns the validated output only on success.ValidatedValue<T>helpers so nested validation does not materializeintermediate failed
Result<T>instances.2.
ValidationContextwas redesigned from a per-scope object into a scoped struct over shared stateReference:
0024-validation-context-optimization.mdOriginal plan:
The original plan described
ValidationContextas the central mutable validation object and expectedIValidationContextFactoryto create both root and child contexts that share an error sink while composing targets.Implemented:
ValidationContextis now areadonly structthat represents a scoped view over a single validation run.A single
ValidationStateinstance owns the run-level mutable state, including options, templates, shared items, anderror accumulation.
IValidationContextFactorywas simplified to root-context creation only. Child scopes are now created directly fromValidationContextthrough explicit APIs:For(...)ForMember(...)ForIndex(...)WithPrefix(...)The error accumulator was folded into
ValidationState. The implementation keeps the first error inline, allocates anowned
Error[10]when the second error arrives, grows that array when necessary, and wraps the used portion directlyinto
ErrorsviaReadOnlyMemory<Error>instead of copying into a second exact-sized array.3. Validation configuration became immutable and policy-based
Reference:
0024-options-and-error-templates-optimization.mdOriginal plan:
The original plan kept
ValidationContextOptionsandValidationErrorTemplatesas separate configuration concepts anddescribed them mainly as holders for normalization settings, automatic-null behavior, and reusable message text.
Implemented:
ValidationContextOptionsandValidationErrorTemplatesare now immutable records with safe-to-reuse defaultinstances.
ValidationErrorTemplatesbecame part ofValidationContextOptions, so one validation run is configured byone immutable options object.
The previous boolean-plus-delegate style configuration was replaced by explicit policy abstractions:
IStringValueNormalizerIAutomaticNullErrorProviderThe options model also grew beyond the original plan to support localization and richer message generation:
CultureInfonow participates in validation-message formatting.ValidationContextKey<T>and read from both mutable and readonlycontext views.
ReadOnlyValidationContextwas introduced so message generation and policy hooks can inspect run-level statewithout boxing the mutable
ValidationContextstruct.4. Error templates now produce richer message descriptors instead of only formatted strings
Reference:
0024-options-and-error-templates-optimization.mdOriginal plan:
The original plan envisioned
ValidationErrorTemplatesas a reusable source of localized format strings and formattinghelpers that future check extensions could use to build
Error.Messagevalues.Implemented:
ValidationErrorTemplatesnow stores template objects (IValidationErrorMessageTemplateandIValidationErrorMessageTemplate<TParameter>) instead of raw strings.Those templates produce
ValidationErrorMessage, a dedicated value type that carries:This goes beyond the original plan because templates can now support constant messages, specialized display-name
formatting, culture-aware parameter formatting, and frontend/localization key scenarios without routing every message
through
string.Format(params object?[]).5. Target normalization kept its public behavior but changed its internal architecture significantly
Reference:
0024-normalization-optimization.mdOriginal plan:
The original plan required a cache-backed
IValidationTargetNormalizerwith configurable casing semantics and stablepath normalization behavior.
Implemented:
The public API and semantics stayed the same, but the cold-path implementation changed substantially.
DefaultValidationTargetNormalizer.NormalizeCorewas rewritten as a single-pass span-based parser that:Substring-based segment cleanupArrayPool<char>buffers for longer pathsThis is an implementation deviation rather than a semantic one: the branch keeps the original normalization contract
while replacing the original allocation profile with a lower-allocation parser.
6. Benchmarks expanded beyond the two endpoint comparisons from the original plan
Reference: all follow-up plans
Original plan:
The original benchmark scope focused on two equivalent Minimal API endpoints compared against FluentValidation
12.1.1: one simple scenario and one more complex scenario.Implemented:
Those endpoint benchmarks still exist, but the benchmark suite was broadened to measure the optimization work
introduced after the original plan:
ValidationEndpointBenchmarks.csstill compares the simple and complex endpoint scenarios against FluentValidation.ValidationContextBenchmarks.csmeasures nested scope creation, error accumulation, and error materialization for1, 2, 10, and more than 10 errors.
ValidationConfigurationBenchmarks.csmeasures string normalization, automatic null-error creation, and messagetemplate generation.
Summary
The original validation-support plan is still recognizable in the implemented package: the new project exists, the
package exposes low-allocation checks, flat validation errors, public target-composition helpers, sync and async
validators, tests, and FluentValidation comparison benchmarks.
The major deviations are architectural. The branch moved away from a public
ValidationOutcome<T>model, redesignedvalidation around scoped
ValidationContextstructs plus sharedValidationState, made configuration immutable andpolicy-driven, upgraded error templates into richer message-template objects, and added focused microbenchmarks to
prove the optimization work.