Discussion: FluentValidation breaking changes going forward
See original GitHub issueFor FluentValidation 10, I’m considering making several breaking changes that I feel will help the library continue to evolve, but I’m not sure whether these changes are necessarily worth it. Over the last 10 years I’ve tried to avoid major breaking changes as FluentValidation has a large user-base, and also breaking changes tend to generate a large number of support requests. As the only developer on this project (which I do in my free time), support requests can be a huge time-sink.
I’d really appreciate others’ opinions of whether this is a worthwhile effort, particularly those who have been involved in similar efforts in other OSS projects. At the moment there are 3 main areas I’m considering which will introduce breaking changes: Generics in the internal model, changes asynchronous validation, and dropping support for older .NET versions.
Generics in the Internal Model.
FluentValidation’s public API (the fluent interface) is strongly-typed with generics. This public API builds an internal model, which does not use generics, and uses object
instances to represent both the root model being validated, as well as the individual property values. This decision was originally made to allow the internal API to be a lot more malleable, and be able to customize/extend behaviour which isn’t necessarily type-safe at compiletime (but would be at runtime).
An example of this is transformations which allow the type of the property for while the rule is defined to be changed to another type.
I would like to change this to use generics all the way through FluentValidation, which will eliminate boxing and provide much more type-safety throughout. The main areas that need changing are PropertyRule
and PropertyValidator
(and all their subclasses).
PropertyRule represents a rule, and is instantiated whenever you call RuleFor
. This would be changed to PropertyRule<T>
, and would receive a ValidationContext<T>
during its Validate methods. I don’t think there are many people this would affect (although I could be wrong). Instantiating a Rule class directly isn’t something that would be done outside of the library (with hindsight, this should not have ever been a public class but back when I started the project I thought it was better that as much as possible was public/virtual. Unfortunately this makes it hard to change the internal model without affecting end users)
As well as the internal model, there are a couple of parts of the public API that would be affected. For years, the way to make a reusable property validator was to create a class that inherits from PropertyValidator
(docs). This would require anyone using this approach to inherit from a new PropertyValidator<T, TProperty>
instead. Anyone accessing the InstanceToValidate
or PropertyValue
properties on PropertyValidatorContext
would also need to change to using PropertyValidatorContext<T, TProperty>
instead.
The fluent interface would also receive an additional type-parameter, representing the current property validator that is active in the chain. This would affect custom extension methods that add to the fluent interface (which is a popular way of extending the library). For example, the WithMessage
method in the library currently looks like this:
public static IRuleBuilderOptions<T, TProperty> WithMessage<T, TProperty>(this IRuleBuilderOptions<T, TProperty> rule, string errorMessage) {
return rule.Configure(config => {
config.CurrentValidator.Options.SetErrorMessage(errorMessage);
});
}
…but would instead become something like the following (note the extra generic)
public static IRuleBuilderOptions<T, TProperty, TValidator> WithMessage<T, TProperty, TValidator>(this IRuleBuilderOptions<T, TProperty, TValidator> rule, string errorMessage) {
return rule.Configure((rule, currentValidator) => {
currentValidator.SetErrorMessage(errorMessage);
});
}
This would affect anyone who has built extension methods on top of IRuleBuilder<T,TProperty>
Asynchronous changes
Covered by https://github.com/FluentValidation/FluentValidation/issues/1481. At the moment it’s too easy to accidentally force async rules to be run synchronously. This change would introduce a separate base class for validators (AsyncAbstractValidator
). Async property validators (such as MustAsync
) and async conditions (WhenAsync
/UnlessAsync
) would only be available in validators that inherit from this class.
Classes that inherit from the new AsyncAbstractValidator
would not be automatically invoked during the ASP.NET MVC/WebApi validation process (as they don’t support async rules).
This is again a big breaking change.
Supporting older .net versions
The main FluentValidation library currently supports netstandard2, meaning it runs on net461 and newer. I’d particularly like to drop support for .NET 4.x, as it’s becoming a pain to support, but I’m not sure what percentage of the FluentValidation userbase is still running on .net 4.x.
I’d also like to make some of the newer c# features (particularly default interface methods, which would make building the fluent interface easier) but these are locked to .net core 3.1 and newer. .net core 2.1 doesn’t reach EOL until late next 2021.
The first 2 of these are a huge amount of work to implement, so I’m hesitating whether or not they’re worth it, and would appreciate others’ feedback.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:2
- Comments:8 (4 by maintainers)
Top GitHub Comments
Yup, for your case that it sounds reasonable to wait with dropping the support for .NET Core 2.1. However, you may also try to publish the desired roadmap - with few months of notice about dropping support (eg. till the end of the year). During that time you may try to get the feedback from users. In the worst case, you’ll just prolong it till August.
Again, they can use the version as they are (you may provide some bug fixes to release branches), but new features will be only available in netstandard 2.1 and netcoreapp 3.1.
@JeremySkinner
That looks like the right way 👍
I assume that it’s fine to restrict support to .NET Standard 2.1 for the regular package. The issue is with ASP.NET packages?
Maybe you could gather from NuGet statistic of how many users are using .NET versions that you don’t want to support? If it appears that there are a lot of them then you could release the last version supporting those versions and inform that from the next version there won’t be official support for them but only per-case paid support (so similar as MS does with .NET and Oracle with Java). Thoughts?