Skip to content
Permalink
main
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
@ldeluigi
Latest commit 7c9ebb2 Feb 21, 2023 History
5 contributors

Users who have contributed to this file

@JeremySkinner @kbilsted @GitHubPang @ldeluigi @matry18

Creating your first validator

To define a set of validation rules for a particular object, you will need to create a class that inherits from AbstractValidator<T>, where T is the type of class that you wish to validate.

For example, imagine that you have a Customer 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<Customer>:

using FluentValidation;

public class CustomerValidator : AbstractValidator<Customer> 
{
}

The validation rules themselves should be defined in the validator class's constructor.

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<Customer>
{
  public CustomerValidator()
  {
    RuleFor(customer => 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 Validate method returns a ValidationResult object. This contains two properties:

  • IsValid - a boolean that says whether the validation succeeded.
  • Errors - a collection of ValidationFailure objects containing details about any validation failures.

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

using FluentValidation.Results; 

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

ValidationResult results = validator.Validate(customer);

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

You can also call ToString on the ValidationResult to combine all error messages into a single string. By default, the messages will be separated with new lines, but if you want to customize this behaviour you can pass a different separator character to ToString.

ValidationResult results = validator.Validate(customer);
string allMessages = results.ToString("~");     // In this case, each message will be separated with a `~`

Note : if there are no validation errors, ToString() will return an empty string.

Chaining validators

You can chain multiple validators together for the same property:

using FluentValidation;

public class CustomerValidator : AbstractValidator<Customer>
{
  public CustomerValidator()
  {
    RuleFor(customer => customer.Surname).NotNull().NotEqual("foo");
  }
}

This would ensure that the surname is not null and is not equal to the string 'foo'.

Throwing Exceptions

Instead of returning a ValidationResult, you can alternatively tell FluentValidation to throw an exception if validation fails by using the ValidateAndThrow method:

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

validator.ValidateAndThrow(customer);

This throws a ValidationException which contains the error messages in the Errors property.

Note ValidateAndThrow is an extension method, so you must have the FluentValidation namespace imported with a using statement at the top of your file in order for this method to be available.

using FluentValidation;

The ValidateAndThrow method is helpful wrapper around FluentValidation's options API, and is the equivalent of doing the following:

validator.Validate(customer, options => options.ThrowOnFailures());

If you need to combine throwing an exception with Rule Sets, or validating individual properties, you can combine both options using this syntax:

validator.Validate(customer, options => 
{
  options.ThrowOnFailures();
  options.IncludeRuleSets("MyRuleSets");
  options.IncludeProperties(x => x.Name);
});

It is also possible to customize type of exception thrown, which is covered in this section.

Complex Properties

Validators can be re-used for complex properties. For example, imagine you have two classes, Customer and Address:

public class Customer 
{
  public string Name { get; set; }
  public Address Address { get; set; }
}

public class Address 
{
  public string Line1 { get; set; }
  public string Line2 { get; set; }
  public string Town { get; set; }
  public string Country { get; set; }
  public string Postcode { get; set; }
}

... and you define an AddressValidator:

public class AddressValidator : AbstractValidator<Address> 
{
  public AddressValidator()
  {
    RuleFor(address => address.Postcode).NotNull();
    //etc
  }
}

... you can then re-use the AddressValidator in the CustomerValidator definition:

public class CustomerValidator : AbstractValidator<Customer> 
{
  public CustomerValidator()
  {
    RuleFor(customer => customer.Name).NotNull();
    RuleFor(customer => customer.Address).SetValidator(new AddressValidator());
  }
}

... so when you call Validate on the CustomerValidator it will run through the validators defined in both the CustomerValidator and the AddressValidator and combine the results into a single ValidationResult.

If the child property is null, then the child validator will not be executed.

Instead of using a child validator, you can define child rules inline, eg:

RuleFor(customer => customer.Address.Postcode).NotNull()

In this case, a null check will not be performed automatically on Address, so you should explicitly add a condition

RuleFor(customer => customer.Address.Postcode).NotNull().When(customer => customer.Address != null)