"Create or modify a CoreEx validator in the Application layer. USE FOR: new Validator<T,TSelf> (no injection), new Validator<T> with constructor injection, AbstractValidator<T,TSelf> (FluentValidation-style), adding rules to an existing validator, nested entity/collection/dictionary validators. DO NOT USE FOR: domain invariants in aggregates, FluentValidation NuGet package, Infrastructure-layer checks."
Recursos
1Instal·la
npx skillscat add avanade/coreex/coreex-validator Instal·la des del registre de SkillsCat.
CoreEx: Validator
Guides you through creating or modifying a CoreEx validator (Application/Validators/) for a contract or request type. Covers declarative rules, ref-data validation, async database checks, collection and dictionary validators.
When to Use
- New validator for a contract or request type (no database calls needed)
- New validator that requires a repository or other Application-layer dependency
- Adding property rules or an async check to an existing validator
- Nested validator for a sub-property (
.Entity()), a collection, or a dictionary - Switching to FluentValidation-compatible
AbstractValidatorsyntax
When Not to Use
- Domain invariants (aggregates, entities, value objects) — those belong in the Domain layer
- External HTTP response-contract validation — use
coreex-adapter; small client-only rules belong inline in the Infrastructure client, notApplication/Validators/ - Other Infrastructure-level data checks that are not accessed via an Application-layer interface
FluentValidationNuGet package —AbstractValidatorhere isCoreEx.Validation.AbstractValidator
Quick Reference
Clarifying questions to ask before writing any code:
- Does the validator need a constructor-injected dependency (e.g. a repository)? (determines base class)
- Which properties need validation? (list upfront — batch type resolution, don't interrupt per-property)
- Are any ref-data properties required? Optional? (
.Mandatory().IsValid()vs.IsValid()) - Are any checks async (e.g. confirming an entity exists in the database)?
- Does the user prefer FluentValidation-style
RuleFor(x => ...)syntax?
Base class decision:
| Situation | Base class | Notes |
|---|---|---|
| No injection needed | Validator<T, TSelf> |
Exposes Default singleton; invoke via .Default.ValidateAndThrowAsync(...) |
| Constructor injection needed | Validator<T> |
No Default; instantiate at the call site with already-injected dependencies: new {Name}Validator(_dep).ValidateAndThrowAsync(...) |
| FluentValidation-style preferred | AbstractValidator<T, TSelf> |
RuleFor(x => ...) / NotEmpty() / IsValid() — still CoreEx; has Default |
Key rules at a glance:
- Ref-data:
.IsValid()on the navigation property (Gender), never on*Codestring Mandatory()on a non-nullable value type errors ondefault(0,MinValue) — use a range rule if0is valid- Runtime-computed thresholds: use delegate overloads (
.LessThanOrEqualTo(_ => ...)) — prefer overOnValidateAsyncimperative logic context.HasErrorsguard before async I/O;context.HasError(x => x.Prop)for per-property guardscontext.AddError(x => x.Prop, ...)— member-access expression, nevernameof(...)- Message text argument is a
{2}suffix substitution — not a full sentence; use.Error("...")to override the whole message - Always generate or update the matching
{Validator}Testsin*.Test.Unit/Validators/— cover conditional/business rules and cross-field logic thoroughly, plus a representative error/success pair for simple rules; skip dedicated boundary tests for framework-guaranteed constraints like length rules (MaximumLength/MinimumLength/Length/String)
For full workflow, rule reference, and code examples see `references/workflow.md`.
Key References
- `/.github/instructions/coreex-validators.instructions.md` — full rule set, comparison operators, localization, DependsOn, DI registration
- `/.github/instructions/coreex-tests.instructions.md` — validator unit test conventions:
Test.Scoped,AssertErrors, expected message text - Related skills: `coreex-app-service` (invokes the validator), `coreex-policy` (async I/O guard sibling for checks a validator cannot do), `coreex-contract` (the type being validated)
- Illustrative examples (CoreEx sample — not present in your project):
- `ProductValidator` + `MovementRequestValidator` — simple, plus injection + dictionary + async
- Shopping validators —
AbstractValidatorstyle, plain request validators - `OrderValidator` — collection validator