Iam using Fluent Validations with CQRS using MediatR library. The following Fluent package versions are used in my solution along with .NET 7 & C#.
FluentValidation - 11.8.1
FluentValidation.DependencyInjectionExtensions - 11.8.1
I have the following validators for "Group" property, a "string" datatype in CreateGroupRequest
and UpdateGroupRequest
.
public class CreateGroupRequest : IRequest<BaseResponse<CreateGroupResponse>>
{
public string Group { get; set; }
}
public class CreateGroupValidator : AbstractValidator<CreateGroupRequest>
{
public CreateGroupValidator(IOptions<FeatureOptions> options)
{
var featureOptions = options.Value;
ClassLevelCascadeMode = CascadeMode.Stop;
RuleFor(r => r.Group).NotNull().WithMessage("Group can't be null.");
RuleFor(r => r.Group).NotEmpty().WithMessage("Group must not be empty.");
RuleFor(r => r.Group).Length(featureOptions.GroupMinLength, featureOptions.GroupMaxLength)
.WithMessage("Group length must be between 1 and 15.");
RuleFor(r => r.Group).Matches(featureOptions.GroupAllowedCharactersRegex)
.WithMessage("Group must start and end with lower case letters or numbers. It may contain \"_\" and \" - \".");
}
}
public class UpdateGroupValidator : AbstractValidator<UpdateGroupRequest>
{
public UpdateGroupValidator(IOptions<FeatureOptions> options)
{
var featureOptions = options.Value;
ClassLevelCascadeMode = CascadeMode.Stop;
RuleFor(r => r.Group).NotNull().WithMessage("Group can't be null.");
RuleFor(r => r.Group).NotEmpty().WithMessage("Group must not be empty.");
RuleFor(r => r.Group).Length(featureOptions.GroupMinLength, featureOptions .GroupMaxLength)
.WithMessage("Group length must be between 1 and 15.");
RuleFor(r => r.Group).Matches(labelingOptions.GroupAllowedCharactersRegex)
.WithMessage("Group must start and end with lower case letters or numbers. It may contain \"_\" and \" - \".");
}
}
I am using ClassLevelCascadeMode = CascadeMode.Stop
setting in the validators.
There are other requests where "Group" property needs to be validated. Is there a way to move all the validations on the "Group" property so the rules are defined at a single place and reused in the validators?
Any help on this is appreciated.
First of all, you can simplify your code grouping all your rules in one declaration:
public CreateGroupValidator(IOptions<FeatureOptions> options)
{
var featureOptions = options.Value;
ClassLevelCascadeMode = CascadeMode.Stop;
RuleFor(r => r.Group).NotNull().WithMessage("Group can't be null.")
.NotEmpty().WithMessage("Group must not be empty.")
.Length(featureOptions.GroupMinLength, featureOptions.GroupMaxLength).WithMessage("Group length must be between 1 and 15.")
.Matches(featureOptions.GroupAllowedCharactersRegex).WithMessage("Group must start and end with lower case letters or numbers. It may contain \"_\" and \" - \".");
}
Same thing for UpdateGroupValidator
.
That said, you can refactor using extension methods:
public static class ValidationExtensions
{
public static IRuleBuilderOptions<TCommand, TProperty?> MustBeAValidGroup<TCommand, TProperty>(this IRuleBuilder<TCommand, TProperty?> myGroup)
{
return myGroup.NotNull().WithMessage("Group can't be null.")
.NotEmpty().WithMessage("Group must not be empty.")
.Length(featureOptions.GroupMinLength, featureOptions.GroupMaxLength).WithMessage("Group length must be between 1 and 15.")
.Matches(featureOptions.GroupAllowedCharactersRegex).WithMessage("Group must start and end with lower case letters or numbers. It may contain \"_\" and \" - \".");
}
}
Then, adding the using of ValidationExtensions
, you can use it in CreateGroupValidator
and UpdateGroupValidator
like:
[...]
RuleFor(r => r.Group).MustBeAValidGroup();
[...]