Fluent Validations - How to reuse rules for a property?

98 Views Asked by At

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.

1

There are 1 best solutions below

1
On BEST ANSWER

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();
[...]