DEV Community

Cover image for FluentValidation inline validate
Karen Payne
Karen Payne

Posted on • Edited on

FluentValidation inline validate

Learn FluentValidation inline validation

One of the most important tasks in software development is to ensure that data saved meets requirements for business solutions. Learn how to use FluentValidation library to validate data for basic solutions using FluentValidation inline validation while for complex validation see the following article which demonstrates in both desktop and web projects.

Note
There is no formal documentation for inline validation. See source code.

Secondary items, using a json file to dynamically read value for validating class/model properties and using custom extensions methods for extending FluentValidation.

Happy face

For those who are use to conventional validation, inline does not change this as shown below.

Sample project



var validator = Person.Validator.Validate(person);


Enter fullscreen mode Exit fullscreen mode

Why using Windows Forms?

First off, the code presented will work in other project types, cross-platform, console and web. The reason for using Windows Forms is it is easy to learn from and work except on a Mac.

Background

The task in this case is to validate a Person class, ensure that the Title property is valid against a list, FirstName and LastName are not empty, Gender is valid against a list and BirthDate fails in a specific range.

When a developer writes code against business specifications there would be no reason for validating Title and Gender against list but in the real world the human factor means code is prone to mistakes or not reading an adhering to specifications which is where validation is needed.

Models



public enum Gender
{
    Male,
    Female,
    NotSet
}


Enter fullscreen mode Exit fullscreen mode
  • Validator is for inline validation
  • The use of an interface, with one model it is not helpful but when dealing with more models and countless projects it becomes more important.
  • Change notification is not always needed yet for some this may be new.


public class Person : IHuman, INotifyPropertyChanged
{
    #region Properties
    private int _id;
    private string _firstName;
    private string _lastName;
    private string _title;
    private DateOnly _birthDate;
    private Gender _gender;

    public int Id
    {
        get => _id;
        set
        {
            if (value == _id) return;
            _id = value;
            OnPropertyChanged();
        }
    }
    public string FirstName
    {
        get => _firstName;
        set
        {
            if (value == _firstName) return;
            _firstName = value;
            OnPropertyChanged();
        }
    }
    public string LastName
    {
        get => _lastName;
        set
        {
            if (value == _lastName) return;
            _lastName = value;
            OnPropertyChanged();
        }
    }
    public string Title
    {
        get => _title;
        set
        {
            if (value == _title) return;
            _title = value;
            OnPropertyChanged();
        }
    }
    public DateOnly BirthDate
    {
        get => _birthDate;
        set
        {
            if (value.Equals(_birthDate)) return;
            _birthDate = value;
            OnPropertyChanged();
        }
    }
    public Gender Gender
    {
        get => _gender;
        set
        {
            if (value == _gender) return;
            _gender = value;
            OnPropertyChanged();
        }
    }

    #endregion

    public static readonly InlineValidator<Person> Validator = new()
        {
            // validate against the Titles from Validation.json using ValidatorExtensions.In extension method
            v => v.RuleFor(x => x.Title)
                .In(Titles),

            v => v.RuleFor(x => x.FirstName)
                .NotEmpty()
                .WithMessage("{PropertyName} is required"),

            v => v.RuleFor(x => x.LastName)
                .NotEmpty()
                .WithMessage("{PropertyName} is required"),

            // validate against the genders from Validation.json
            v => v.RuleFor(x => x.Gender)
                .NotNull()
                .In(GenderTypes),

            // validate against the BirthDateRule extension method
            v => v.RuleFor(x => x.BirthDate)
                .BirthDateRule()

        };



    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}


Enter fullscreen mode Exit fullscreen mode

Inline validation

Normally validation is written in a separate class than the business class/model for separation of concerns along with in some cases placing the validation code in a class project for reuse.

The only reason for using inline validation is for smaller projects and for learning purposes.

Usually this is how full validation is configured. The code below was used in this article and is intended for normal style validation while inline is more quick and dirty, smaller project validation.



public class Person
{
    public string UserName { get; set; }
    public string EmailAddress { get; set; }
    public string Password { get; set; }
    public string PasswordConfirmation { get; set; }
    public string PhoneNumber { get; set; }
}


Enter fullscreen mode Exit fullscreen mode


public class UserNameValidator : AbstractValidator<Person>
{
    public UserNameValidator()
    {
        RuleFor(person => person.UserName)
            .NotEmpty()
            .MinimumLength(3);
    }
}

public class PasswordValidator : AbstractValidator<Person>
{

    public PasswordValidator()
    {

        RuleFor(person => person.Password.Length)
            .GreaterThan(7);

        RuleFor(person => person.Password)
            .Equal(p => p.PasswordConfirmation)
            .WithState(x => StatusCodes.PasswordsMisMatch);

    }
}

public class EmailAddressValidator : AbstractValidator<Person>
{
    public EmailAddressValidator()
    {
         RuleFor(person => person.EmailAddress)
            .Must((person, b) => new EmailAddressAttribute().IsValid(person.EmailAddress));
    }
}

public class PhoneNumberValidator : AbstractValidator<Person>
{
    public PhoneNumberValidator()
    {
        RuleFor(person => person.PhoneNumber)
            .MatchPhoneNumber();
    }
}

public class PersonValidator : AbstractValidator<Person>
{
    public PersonValidator()
    {
        Include(new UserNameValidator());
        Include(new PasswordValidator());
        Include(new EmailAddressValidator());
        Include(new PhoneNumberValidator());
    }
}


Enter fullscreen mode Exit fullscreen mode

Implement in a project

Create a model/class



public class Customers
{
    public int CustomerIdentifier { get; set; }
    public string CompanyName { get; set; }
    public string Street { get; set; }
    public string City { get; set; }
    public string PostalCode { get; set; }
    public string Phone { get; set; }
}


Enter fullscreen mode Exit fullscreen mode

Add the InlineValidator as follows.



public class Customers
{
    public int CustomerIdentifier { get; set; }
    public string CompanyName { get; set; }
    public string Street { get; set; }
    public string City { get; set; }
    public string PostalCode { get; set; }

    public static readonly InlineValidator<Customers> Validator = new()
    {
        v => v.RuleFor(x => x.CompanyName)
            .NotEmpty()
            .WithMessage("{PropertyName} is required"),

        v => v.RuleFor(x => x.Street)
            .NotEmpty()
            .WithMessage("{PropertyName} is required"),

        v => v.RuleFor(x => x.City)
            .NotEmpty()
            .WithMessage("{PropertyName} is required"),

        v => v.RuleFor(x => x.PostalCode)
            .NotEmpty()
            .WithMessage("{PropertyName} is required"),

    };

}


Enter fullscreen mode Exit fullscreen mode

Validate the model.

With a instance, in this case Customers call the validator.



Customers customers = new Customers();
var customerValidator = Customers.Validator.Validate(customers);
if (customerValidator.IsValid)
{
    // good to go
}
else
{
    // validation failed
}


Enter fullscreen mode Exit fullscreen mode

In a ASP.NET Core project (see example in source repository) if validation fails return the page.



public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }
}


Enter fullscreen mode Exit fullscreen mode

In Windows forms, use the error provider.

Windows forms sample

Blazor

See the following documentation for how to validate.

Top comments (1)

Collapse
 
jangelodev profile image
João Angelo

Hi Karen Payne,
Top, very nice and helpful !
Thanks for sharing.