Apply same validation rules on different classes with FluentValidation

In this blog post I will explain how to apply the same validation rules on the same properties in different classes with FluentValidation. This post will continue on the previous one where I explained how to create Custom Validators for your properties.

So in the previous example we had the Person class with a PersonValidator class. Let’s say you have some pages in your application to create and edit instances of that Person class. In order to create those pages, we use separate ViewModels for those pages. So let’s say you have a PersonCreateViewModel and a PersonEditViewModel. In this way, you have 3 classes with the same validation rules, because in example the property FirstName is the same in all those classes. If the validation rules of the FirstName changes (in example the MaxLength changes) you have to change the rules on 3 different places. If you forget to change it on one place a new bug is introduced.

Reuse validators for property

In order to reuse the validators we are going to extend the static CustomValidators class from our previous post. Again we are creating an extension method but now for the FirstName property. We put all the validation rules that we have for this FirstName in this custom validator. The end result will than be the following:

public static IRuleBuilderOptions<T, string> FirstNameValidation<T>(this IRuleBuilder<T, string> rule)
{
    return rule
        .NotEmpty()
        .NotNull()
        .MaximumLength(30)
        .NotStartWithWhiteSpace()
        .NotEndWithWhiteSpace();
}

We can now change the PersonValidator (and PersonCreateViewModel and PersonEditViewModel) to use the power of the new FirstNameValidation extension method. The end result will than be the following:

public class PersonValidator : AbstractValidator<Person>
{
    public PersonValidator()
    {        
        RuleFor(e => e.FirstName).FirstNameValidation();
        RuleFor(e => e.LastName).LastNameValidation();
    }
}

The PersonValidator class is now smaller and easier to read. The cool thing as well is that you can combine your custom FirstNameValidation extension method with your other extension methods as well. So when you have in example slightly different validation rules for your create and edit viewmodels you can use in example the FirstNameValidation method for the generic rules and add the specific rules in the particular validator class. See the following example where the edit viewmodel has extra validation rules:

public class PersonCreateViewModelValidator : AbstractValidator<PersonCreateViewModel>
{
    public PersonValidator()
    {        
        RuleFor(e => e.FirstName).FirstNameValidation();
        RuleFor(e => e.LastName).LastNameValidation();
    }
}

public class PersonEditViewModelValidator : AbstractValidator<PersonEditViewModel>
{
    public PersonValidator()
    {        
        RuleFor(e => e.FirstName).FirstNameValidation().NotContainWhiteSpace();
        RuleFor(e => e.LastName).LastNameValidation();
    }
}

Conclusion

Reusing validators saves you a lot of time and duplicate code. This will eventually result in less bugs. Nice is as well that your validator classes like the PersonValidator class is easier to read because it isn’t that long.

20 thoughts to “Apply same validation rules on different classes with FluentValidation”

  1. the article is great but looks like you have missed some code some where.I couldn’t implement it. It would be great if you can post some working example. Thanks

  2. where to create this and how to use
    public static IRuleBuilderOptions FirstNameValidation(this IRuleBuilder rule)
    {
    return rule
    .NotEmpty()
    .NotNull()
    .MaximumLength(30)
    .NotStartWithWhiteSpace()
    .NotEndWithWhiteSpace();
    }

    can you please post complete working code?

    1. If you put the FirstNameValidation in the CustomValidator.cs class (see previous blog post about custom validators).
      public static class {
      public static IRuleBuilderOptions FirstNameValidation(this IRuleBuilder rule)
      {
      return rule
      .NotEmpty()
      .NotNull()
      .MaximumLength(30)
      .NotStartWithWhiteSpace()
      .NotEndWithWhiteSpace();
      }
      }

      You can use the FirstNameValidation in the validator of your choice. For in example the PersonEditViewModelValidator with the following code:

      public class PersonEditViewModelValidator : AbstractValidator {
      public PersonValidator()
      {
      RuleFor(e => e.FirstName).FirstNameValidation().NotContainWhiteSpace();
      RuleFor(e => e.LastName).LastNameValidation();
      }
      }

      (Combined with your comment below)
      Have you put the CustomValidator class in another namespace? If so, add an using in the PersonEditViewModelValidator class. Otherwise the extension method FirstNameValidation isn’t showing up. (as you describe)

  3. I have created a static class inside it added custom rule FirstNameValidation and tryingg to use it but cannot access it like rulefor(a=>a).FirstNameValidation however i can access it like staticclass.FirstNameValidation

  4. Thanks for your quick reponse . I appreciate it.

    yes, custom validator and calling fluent validation are in same namespace still not working
    can you please post working solution?

  5. Hi Ralph,

    It is working. I was able to implement it with your inputs.
    You are the best. I had posted questions on many blogs, but you are the only one replied.

    Keep up the good work.

    Thanks

    1. Great to hear! Can you share what fixed your problem? Maybe I can update the post or maybe someone else has the same problem?

  6. What I should do in cases where I have 2 view models, but they use the same validation?
    Do I need to copy & paste the same validation, changing the AbstractValidator?

    1. If I understand you correct, you have 2 viewmodels, that have the same validation logic. Why do you have then 2 viewmodels? Is that because one viewmodel has more properties (without validation) than the other? So in context of the sample above. Let’s say PersonCreateViewModel and PersonEditViewModel have the same validation logic and you don’t want to duplicate that. Create a base class called PersonManageViewModel and put the AbstractValidator on that. Then inherit PersonCreateViewModel and PersonEditViewModel from the PersonManageViewModel class. Change the validation in the create and edit vm to “public class PersonCreateViewModelValidator : PersonManageViewModelValidator” In your PersonManageViewModel you have a validator like this: “public class PersonManageViewModelValidator : AbstractValidator where T : PersonManageViewModel”
      Of course you need to move the properties that are used in both viewmodels to the PersonManageViewModel.

  7. Maybe I won’t be there in time, but …

    I have 30 different models (configuration tables from a large application), but with the same structure (int, string); the name of the 2 properties may vary (Id / Description, SpecializationId / SpecializationName, ..). The validations I intend to make are the same for all models: NotEmpty () / NotNull (), min/max length. I use Fluent Validation with .Net Core. I would like to know (if possible) how to create a generic Validator for the 30 models. Thanks in advance

    1. So you want to create one validator for 30 models. The properties of the models have different names but the logic is the same… That is difficult. Not really the idea of this FluentValidation I think. One possible solution might be is to implement some kind of base class or abstract class with 2 properties (like id and description) and you make the validation logic on those two properties. Then in your 30 models, you implement those properties and connect the values to the original properties. BUT this of course als have his down side….

  8. Thanks for your great article. I want to expand the idea to create a custom validator for my field UsernameOrEmail, i.e. I need to provide different validations depending on the content of the field. Is is a username or and email? How to specify code to distinguish?

    Here is some (pseudo)code:

    public static IRuleBuilder UsernameOrEmail(this IRuleBuilder ruleBuilder)
    {
    return ruleBuilder
    .NotEmpty()

    .When( true, () => //in case UsernameOrEmail is an eMail – check if string is not null and string contains the char ‘@’
    // ??? no clue here about the required syntax. Somthing like this: em => em != null && em.Contains(‘@’), () => ???
    {
    .Email() //perform validation for an email address
    }
    ).Otherwise( () => //it is a username
    {
    .Username() //perform validation for a username
    });
    }

    1. Hi,
      I think you are close. To determine if it is an email address try to use a regular expression for that. In that case you know that it is an email address otherwise it is an username.
      But why do you wanna check that? I think empty and max length are enough in those situations. Then check if you have a valid match for username or email with the corresponding password.

      1. HI Ralph, thanks for your reply. Going for some classical checks is feasible, what I want to do is to delegate all this validation stuff to the cool FluentValidation. Do you think you could give me a hint on how to make my (pseudo)code runnable? Yes, I feel I’m not too far away from the solution, but I’m stuck.

        1. I think I already did that… Use the regex for if else. You can do it ;-).
          Not really have time to create a sample…

  9. Hi Ralph,
    In order to deal with fields that are not required but need to still have max length enforced, what about the following using an interface to decorate? Do you have a better way to do this?

    public record ItemData(string Name, string Description): IData;

    public interface IData
    {
    public string Name {get; init;}
    public string Description {get; init;}
    }

    public class ItemDataValidator : AbstractValidator
    {
    public ItemDataValidator()
    {
    RuleFor(data => data.Name).NameValidationRule();
    RuleFor(data => data.Description).DescriptionValidationRule().DescriptionNotRequiredRule();
    }
    }

    public static class RuleExtensions
    {
    public static IRuleBuilderOptions NameValidationRule(this IRuleBuilder ruleBuilder)
    {
    return ruleBuilder.NotEmpty().MaximumLength(64);
    }

    public static IRuleBuilderOptions DescriptionValidationRule(this IRuleBuilder ruleBuilder)
    {
    return ruleBuilder.MaximumLength(5);
    }

    public static IRuleBuilderOptions DescriptionNotRequiredRule(this IRuleBuilderOptions ruleBuilder) where T: IData
    {
    return ruleBuilder.Unless(b => b.Description.Length == 0);
    }

    }

    1. Well I’m not really creating the whole day validators but I think you have a good solution here. But do you have any problems with this? If not, it’s good right

Leave a Reply