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)