Intent.Application.Mediator.FluentValidation
Adds FluentValidation to the commands and queries of Intent.Application.Mediator. It generates a validator for every Command and Query, and a validation step in the Mediator request pipeline that runs those validators before the handler.
What This Module Generates
| Output | What it is |
|---|---|
CommandValidator |
An AbstractValidator<TCommand> for each Command in the Services designer. |
QueryValidator |
An AbstractValidator<TQuery> for each Query in the Services designer. |
ValidationBehaviour<TRequest, TResponse> |
A Mediator pipeline behaviour (Common/Behaviours) that runs the validators registered for the request. |
IBypassValidation |
A marker interface (Common/Interfaces) that lets a request opt out of validation. |
It also adjusts the core's UnhandledExceptionBehaviour when that behaviour is enabled (see below).
Installation
Installing Intent.Application.FluentValidation into an application that already has Intent.Application.Mediator installs this module as well (the core module declares it as interoperability). It requires Intent.Application.Mediator; install the core first if you add this module by hand.
The module has no settings of its own. The unique constraint validation default is read from the Intent.Application.FluentValidation application settings.
Validators
Command and query validators are built on the same shared validator base used by the MediatR and Wolverine FluentValidation modules, so the rules generated from the designer (required, length, ranges, unique constraints and so on) match those modules. Validators may inject repositories (for example for unique constraint checks), and the custom validation hook is enabled so you can add bespoke rules that survive regeneration. In the generated rules, the lambda parameter is named command for commands and query for queries.
The custom validation AI skill (fluent-validation-custom-validation) is owned by Intent.Application.FluentValidation and is shared by every transport; this module does not generate its own copy.
The Validation Pipeline Behaviour
ValidationBehaviour<TRequest, TResponse> implements Mediator's IPipelineBehavior. For each request it:
- Returns straight to the next step if the request implements
IBypassValidation. - Otherwise, if any
IValidator<TRequest>is registered, validates the request with each validator and collects every failure. - Throws a single
FluentValidation.ValidationExceptioncarrying all the failures if there were any. - Otherwise continues to the next step in the pipeline.
A request with no registered validators is passed through untouched.
Where it sits in the pipeline
You do not register this behaviour yourself. Intent.Application.Mediator owns the whole ordered list of pipeline behaviours and adds the validation step when it finds the Application.Common.ValidationBehaviour template role, which this module fills. The fixed order is, outermost first: Logging, UnhandledException, Performance, Authorization, MessageBusPublish, Validation, UnitOfWork. Validation therefore runs after authorization and before the unit of work is opened, so an invalid command never starts a transaction.
Validators run one at a time
The validators for a request run sequentially, never in parallel. Asynchronous rules, such as unique constraint checks, use the scoped DbContext, and a DbContext does not support concurrent operations. Running them with something like Task.WhenAll would make validation intermittently fail with concurrency errors in applications that use Entity Framework Core.
Bypassing validation
IBypassValidation is an empty marker interface. A command or query that implements it skips all validators. Use it for specialised messages where pipeline validation is redundant or deferred to a later stage. The module only generates the interface; it does not add it to any request.
Validation Failures Are Not Logged As Unhandled Errors
If the core's optional UnhandledExceptionBehaviour is enabled (a setting in Intent.Application.Mediator), this module inserts a catch (ValidationException) { throw; } block ahead of the behaviour's general catch (Exception). Validation failures are expected request failures, so without this block every rejected request would be logged as an unhandled exception. The exception is still rethrown, so it propagates up the pipeline to the caller unchanged. If the setting is off, the behaviour does not exist and nothing is changed.
Dependencies
Intent.Application.Mediator- owns the pipeline, the command and query contracts the validators are keyed to, and theUnhandledExceptionBehaviourthis module adjusts.Intent.Application.FluentValidation- shared FluentValidation settings, registration and the custom validation AI skill.Intent.Modelers.ServicesandIntent.Modelers.Services.CQRS- the Command and Query designer elements.
Related Modules
- Intent.Application.FluentValidation - the shared FluentValidation module.
- Intent.Application.MediatR.FluentValidation - the equivalent module for MediatR.
- Intent.Application.Wolverine.FluentValidation - the equivalent module for Wolverine.
Intent.Application.Mediator- the core module of this family (no published page yet).