11.0 Upgrade Guide¶
Introduction¶
FluentValidation 11.0 is a major release that included several breaking changes. Please review this document carefully before upgrading from FluentValidation 10.x to 11.
There were 3 main goals for this release:
- Removing deprecated code and support for obsolete platforms
- Update sync-over-async workflows to clearly throw an exception
- Remove ambiguity in handling of
CascadeMode
settings
Below is a summary of all the changes in this release:
Changes in supported platforms¶
- .NET Core 2.1 is no longer supported as Microsoft has stopped support for this platform.
Sync-over-async now throws an exception¶
In FluentValidation 10.x and older, if you attempted to run an asynchronous validator synchronously, the asynchronous rules would silently be run synchronously. This was unintutive and would lead to deadlocks.
Starting in FluentValidation 11.0, validators that contain asynchronous rules will now throw a AsyncValidatorInvokedSynchronouslyException
if you attempt to invoke them synchronously. You must invoke these validators asynchronously.
This affects rules that contain any of the following:
- Calls to
MustAsync
- Calls to
WhenAsync
andUnlessAsync
- Calls to
CustomAsync
- Use of any custom async validators
OnFailure and OnAnyFailure removed¶
The deprecated methods OnFailure
and OnAnyFailure
have been removed.
These were callbacks that could be used to define an action that would be called when a particular rule fails. These methods were deprecated in 10.x as they allowed the standard FluentValidation workflow to be bypassed, and additionally they have caused various maintenance issues since they were introduced.
If you were previously using OnFailure
or OnAnyFailure
to perform custom logic after validation, we recommend using a Custom
validator instead.
Test Helper changes¶
The deprecated extension methods validator.ShouldHaveValidationErrorFor
and validator.ShouldNotHaveValidationErrorFor
have been removed. The recommended alternative is to use TestValidate
instead, which is covered in the documentation here.
Cascade Mode Changes¶
The CascadeMode
properties on AbstractValidator
and ValidatorOptions.Global
have been deprecated and replaced with the properties RuleLevelCascadeMode
and ClassLevelCascadeMode
which provide finer-grained control for setting the cascade mode.
If you are currently setting ValidatorOptions.Global.CascadeMode
to Continue
or Stop
, you can simply replace this with
ValidatorOptions.Global.DefaultClassLevelCascadeMode = CascadeMode.<YourCurrentValue>;
ValidatorOptions.Global.DefaultRuleLevelCascadeMode = CascadeMode.<YourCurrentValue>;
If you are currently setting it to StopOnFirstFailure
, replace it with
ValidatorOptions.Global.DefaultClassLevelCascadeMode = CascadeMode.Continue; // Not actually needed as this is the default. Just here for completeness.
ValidatorOptions.Global.DefaultRuleLevelCascadeMode = CascadeMode.Stop;
Similarly, if you are currently setting AbstractValidator.CascadeMode
to Continue
or Stop
, replace this with
ClassLevelCascadeMode = CascadeMode.<YourCurrentValue>;
RuleLevelCascadeMode = CascadeMode.<YourCurrentValue>;
If you are currently setting it to StopOnFirstFailure
, replace it with
ClassLevelCascadeMode = CascadeMode.Continue;
RuleLevelCascadeMode = CascadeMode.Stop;
If you are calling .Cascade(CascadeMode.StopOnFirstFailure)
in a rule chain, replace StopOnFirstFailure
with Stop
(this has always had the same behavior at rule-level since Stop
was introduced anyway).
All of the changes described above are exactly what the code does now anyway - e.g. if you set AbstractValidator.CascadeMode
to Stop
, it sets AbstractValidator.DefaultRuleLevelCascadeMode
and AbstractValidator.DefaultClassLevelCascadeMode
to Stop
, and doesn’t use AbstractValidator.CascadeMode
in any logic internally.
You may also be able to remove some now-unneeded calls to .Cascade
at rule-level. For example, if you have the cascade mode at validator class-level set to Continue
, and are repeating .Cascade(CascadeMode.Stop[/StopOnFirstFailure])
for each rule, you can now replace this with
ClassLevelCascadeMode = CascadeMode.Continue;
RuleLevelCascadeMode = CascadeMode.Stop;
…or their global default equivalents.
See this page in the documentation for details of how cascade modes work and the reasons for this change.
As StopOnFirstFailure
is deprecated and scheduled for removal, it cannot be assigned to either of the two new AbstractValidator
properties or their global equivalents (it still can be assigned to the also-deprecated AbstractValidator.CascadeMode
). Attempting to set the new properties to StopOnFirstFailure
will simply result in Stop
being used instead.
MessageBuilder changes¶
If you use the MessageBuilder
functionality to provide custom logic for error message creation then please note that as of 11.0 you can only have a single MessageBuilder
associated with a rule chain. This property is also now set-only. In previous versions you may have had code like this:
return ruleBuilder.Configure(rule => {
var originalMessageBuilder = rule.MessageBuilder;
rule.MessageBuilder = context => {
// ... some custom logic in here.
return originalMessageBuilder?.Invoke(context) ?? context.GetDefaultMessage();
};
});
Now as this property is set-only you’ll need to update it to remove references to originalMessageBuilder
:
return ruleBuilder.Configure(rule => {
rule.MessageBuilder = context => {
// ... some custom logic in here.
return context.GetDefaultMessage();
};
});
This means you can no longer chain MessageBuilders together, and whichever one is set last will be the only one associated with the rule, so please confirm that you aren’t relying on the previous behaviour before making this change.
ASP.NET Core Integration changes¶
The deprecated property RunDefaultMvcValidationAfterFluentValidationExecutes
within the ASP.NET Configuration has been removed.
If you were making use of this property, you should use DisableDataAnnotationsValidation
instead. Note that this property is the inverse of the previous behaviour:
// Before:
services.AddFluentValidation(fv => {
fv.RunDefaultMvcValidationAfterFluentValidationExecutes = false;
});
// After:
services.AddFluentValidation(fv => {
fv.DisableDataAnnotationsValidation = true;
});
Removal of backwards compatibility property validator layer¶
The non-generic PropertyValidator
class (and associated classes/helpers) have been removed. These classes were deprecated in 10.0. If you are still using this class, you should migrate to the generic PropertyValidator<T,TProperty>
instead.
Internal API Changes¶
Several of the methods in the Internal API have been removed. These changes don’t affect use of the public fluent interface, but may impact library developers or advanced users.
IValidationRule<T,TProperty>.CurrentValidator
has been removed (use theCurrent
property instead) -IValidationRule<T,TProperty>.Current
now returns anIRuleComponent<T,TProperty>
interface instead ofRuleComponent<T,TProperty>
(necessary to support variance) -IValidationRule<T,TProperty>.MessageBuilder
’s argument is now anIMessageBuilderContext<T,TProperty>
interface instead ofMessageBuilderContext<T,TProperty>
class (necessary to support variance)IValidationRule<T,TProperty>.MessageBuilder
is now set-only, and has no getter exposed (needed to support variance), meaning you can only have one message builder per rule chain.IRuleComponent<T,TProperty>.CustomStateProvider
is now set-only to support varianceIRuleComponent<T,TProperty>.SeverityProvider
is now set-only to support varianceGetErrorMessage
is no longer exposed onIRuleComponent<T,TProperty>
- Remove deprecated
Options
property fromRuleComponent
- The
MemberAccessor
class has been removed as it’s no longer used