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.

Creating custom validators with FluentValidation

This blog post will explain how creating custom validators with FluentValidation. A while back I wrote a blog post about how to start with FluentValidation in your project. In this post we will continue on that foundation.

Let’s say in example you have the class Person.

public class Person {
    public int Id { get; set; }     
    public string FirstName { get; set; }     
    public string LastName { get; set; }     
    public DateTime BirthDay { get; set; } 
}

You need to validate that Person class. So let’s say, you want to validate the FirstName and LastName property. Those properties are similar to each other because both are name and a string so both could have the same (custom) validators. 

So let’s create a PersonValidator class which of course will validate the Person class.

public class PersonValidator : AbstractValidator<Person>
{     
    public PersonValidator()  
    {                 
        RuleFor(e => e.FirstName).NotEmpty().MaximumLength(30);
        RuleFor(e => e.LastName).NotEmpty().MaximumLength(30);
    } 
}

Custom Validator

Now, you want to extend the basic validators. So let’s say you want a validator that the name must not start with a whitespace. You can validate this on multiple ways but the most logical way is to create a custom validator once and use that validator on multiple places.

Here is the validator for checking whitespaces in the begin or end of a string.

public static class CustomValidators 
{     
    public static IRuleBuilderOptions<T, string> NotStartWithWhiteSpace<T>(this IRuleBuilder<T, string> ruleBuilder)     
    {         
        return ruleBuilder.Must(m => m != null && !m.StartsWith(" ")).WithMessage("'{PropertyName}' should not start with whitespace");     
    }     
        
    public static IRuleBuilderOptions<T, string> NotEndWithWhiteSpace<T>(this IRuleBuilder<T, string> ruleBuilder)     
    {         
        return ruleBuilder.Must(m => m != null && !m.EndsWith(" ")).WithMessage("'{PropertyName}' should not end with whitespace");     
    } 
}

You can use the custom validators in the PersonValidator in the following way:

public class PersonValidator : AbstractValidator<Person> 
{     
    public PersonValidator()     
    {                 
        RuleFor(e => e.FirstName).NotEmpty().MaximumLength(30).NotStartWithWhiteSpace().NotEndWithWhiteSpace();         
        RuleFor(e => e.LastName).NotEmpty().MaximumLength(30).NotStartWithWhiteSpace().NotEndWithWhiteSpace();     
    } 
}

With this above custom validator is validating your objects very easy to do.

In the next blog post we go one step further. I will then show you how to apply the same validation rules on multiple classes.

FluentValidation in ASP.NET Core

FluentValidation is a wonderful validation package that is around for years. Last week I was busy with a new application in ASP.NET core. I wanted to add some validation and didn’t used FluentValidation in ASP.NET Core before. So I wanted to see if things where changed. This blog post contains some examples from the official FluentValidation Getting started documentation. In the next blog posts I will go into deeper validation of properties and reusing validators in different models.

Installation

For integration with ASP.NET Core, install the FluentValidation.AspNetCore package:

Install-Package FluentValidation.AspNetCore

Basic validation

Using the package is very easy. Let’s say you have to following class:

public class Customer {
  public int Id { get; set; }
  public string Surname { get; set; }
  public string Forename { get; set; }
  public decimal Discount { get; set; }
  public string Address { get; set; }
}

You would define a set of validation rules for this class by inheriting from AbstractValidator:

using FluentValidation; 

public class CustomerValidator : AbstractValidator&lt;Customer&gt; {
}

To specify a validation rule for a particular property, call the RuleFor method, passing a lambda expression that indicates the property that you wish to validate. For example, to ensure that the Surname property is not null, the validator class would look like this:

using FluentValidation;

public class CustomerValidator : AbstractValidator&lt;Customer&gt; {
  public CustomerValidator() {
    RuleFor(customer =&gt; customer.Surname).NotNull();
  }
}

To run the validator, instantiate the validator object and call the Validate method, passing in the object to validate.

Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator();

ValidationResult result = validator.Validate(customer);

The following code would write any validation failures to the console:

if(! results.IsValid) {
  foreach(var failure in results.Errors) {
    Console.WriteLine("Property " + failure.PropertyName + " failed validation. Error was: " + failure.ErrorMessage);
  }
}

Deeper validation

In the next blog posts I will go into deeper validation with custom validators for properties and reusing validators in different models.