docs/upgrading-to-9.md
FluentValidation 9.0 is a major release that included several breaking changes. Please review this document before upgrading from FluentValidation 8.x to 9.
Support for the following platforms has been dropped:
FluentValidation still supports netstandard2 and net461, meaning that it'll run on .NET Core 2.0 or higher (3.1 recommended), or .NET Framework 4.6.1 or higher.
FluentValidation.AspNetCore requires .NET Core 2.1 or 3.1 (3.1 recommended).
Integration with MVC5/WebApi 2 is no longer supported - both the FluentValidation.Mvc5 and FluentValidation.WebApi packages were deprecated with the release of FluentValidation 8, but they will now no longer receive further updates. They will continue to run on .NET Framework 4.6.1 or higher, but we recommend migrating to .NET Core as soon as possible.
FluentValidation supports 2 methods for validating email addresses.
The first is compatible with .NET Core's EmailAddressAttribute and performs a simple check that an email address contains an @ character. The second uses a regular expression that is mostly compatible with .NET 4.x's EmailAddressAttribute, which also used a regular expression.
In FluentValidation 8 and older, the regex-based email validation was the default. As of 9.0, the ASP.NET Core-compatible email validator is now the default. This change was made to be consistent with ASP.NET Core's default behaviour.
If you still want to validate email addresses using the old regular expression, you can specify RuleFor(customer => customer.Email).EmailAddress(EmailValidationMode.Net4xRegex);. This will give a deprecation warning.
See the documentation on the email validator for more details on why regular expressions shouldn't be used for validating email addresses.
The TestHelper has been updated with several syntax improvements. It is now possible to chain additional assertions on to ShouldHaveValidationErrorFor and ShouldNotHaveValidationErrorFor, eg:
var validator = new InlineValidator<Person>();
validator.RuleFor(x => x.Surname).NotNull().WithMessage("required");
validator.RuleFor(x => x.Address.Line1).NotEqual("foo");
// New advanced test syntax
var result = validator.TestValidate(new Person { Address = new Address()) };
result.ShouldHaveValidationErrorFor(x => x.Surname).WithMessage("required");
result.ShouldNotHaveValidationErrorFor(x => x.Address.Line1);
See the documentation for full details on the Test Helper
FluentValidation 4.x-8.x contained a bug where using NotEqual/Equal on string properties would perform a culture-specific check, which would lead to unintented results. 9.0 reverts the bad change which introduced this several years ago. An ordinal string comparison will now be performed instead.
See the documentation for further details.
The IValidator.Validate(object model) overload has been removed to improve type safety. If you were using this method before, you can use the overload that accepts an IValidationContext instead:
var context = new ValidationContext<object>(model);
var result = validator.Validate(context);
The non-generic ValidationContext has been removed. Anywhere that previously used this class will either accept a ValidationContext<T> or a non-generic IValidationContext interface instead. If you previously made use of this class in custom code, you will need to update it to use one of these as appropriate.
The Transform method can now be used to transform a property value to a different type prior to validation occurring. See the documentation for further details.
Prior to 9.0, changing a rule's severity required hard-coding the severity:
RuleFor(x => x.Surname).NotNull().WithSeverity(Severity.Warning);
Alternatively, this can now be generated from a callback, allowing the severity to be dynamically determined:
RuleFor(x => x.Surname).NotNull().WithSeverity(x => Severity.Warning);
The algorithm used by the ScalePrecision validator has been updated to match SQL Server and other RDBMS systems. The algorithm now correctly checks how many digits are to the left of the decimal point, which it didn't do before.
The ChildvalidatorAdaptor and IncludeRule classes now have generic type parameters. This will not affect users of the public API, but may affect anyone using the internal API.
Older versions of FluentValidation allowed inferring a property's name from the presence of the [Display] or [DisplayName] attributes on the property. This behaviour has been removed as it causes conflicts with ASP.NET Core's approach to localization using these attributes.
If you want to preserve this old behaviour, you can use a custom display name resolver which can be set during your application's startup routine:
FluentValidation.ValidatorOptions.DisplayNameResolver = (type, memberInfo, expression) => {
return memberInfo.GetCustomAttribute()?.GetName();
};
The {ComparisonProperty} error message placeholder (used in various validators that compare two properties, such as LessThanOrEqual) is now formatted consistently with the {PropertyName} placeholder, so PascalCased property names will be split.
Renamed the PropertyValidator.ShouldValidateAsync method to ShouldValidateAsynchronously to indicate that this is not an async method, which is usually denoted by the Async suffix.
This is only relevant if you use RESX-based localization with strongly-typed wrapper classes generated by Visual Studio. Older versions of FluentValidation allowed the use of specifying a resource name and resource type in a call to WithLocalizedMessage:
RuleFor(x => x.Surname).NotNull().WithLocalizedMessage(typeof(MyLocalizedMessages), "SurnameRequired");
This syntax has been superceded by the callback syntax. To access the localized messages with a strongly-typed wrapper, you should now explicitly access the wrapper property inside a callback:
RuleFor(x => x.Surname).NotNull().WithMessage(x => MyLocalizedMessages.SurnameRequired);
Note that support for localization with IStringLocalizer is unchanged.
Full documentation on localization.
SetCollectionValidator has been removed. This was deprecated in 8.0.
Several other methods/properties that were deprecated in FluentValidation 8 have been removed in 9.0.
ReplacePlaceholderWithValue and GetPlaceholder from MesageFormatterResourceName and ResourceType have been removed from IStringSource.ResourceName has been removed from ValidationFailure.Instance was removed from PropertyValidatorContext - use InstanceToValidate instead.DelegatingValidator has been removedFluentValidation.Internal.Comparer has been removedFluentValidation.Internal.TrackingCollection is now internal