SimonCropp / ExtendedFluentValidation

Extends FluentValidation with some more opinionated rules.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ExtendedFluentValidation

Build status NuGet Status

Extends FluentValidation with some more opinionated rules and extensions.

See Milestones for release notes.

Nuget

https://nuget.org/packages/ExtendedFluentValidation/

Extra Rules

Nullability

It leverages nullability information to make all non-nullable reference properties to be required.

Dates

DateTime, DateTimeOffset, and DateOnly cannot be MinValue.

Strings

String cannot be String.Empty or only white-space.

Guids

Guids cannot be Guid.Empty.

Lists/Collections

Lists and Collection cannot be empty.

Usage

There are two ways of applying the extended rules.

ExtendedValidator

Using a base class ExtendedValidator:

class PersonValidatorFromBase :
    ExtendedValidator<Person>
{
    public PersonValidatorFromBase()
    {
        //TODO: add any extra rules
    }
}

snippet source | anchor

AddExtendedRules

Using an extension method AddExtendedRules:

class PersonValidatorNonBase :
    AbstractValidator<Person>
{
    public PersonValidatorNonBase() =>
        this.AddExtendedRules();
    //TODO: add any extra rules
}

snippet source | anchor

Equivalent

The above are equivalent to:

public class Person
{
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public string? MiddleName { get; set; }
    public string FamilyName { get; set; }
    public DateTimeOffset Dob { get; set; }
}

snippet source | anchor

class PersonValidatorEquivalent :
    AbstractValidator<Person>
{
    public PersonValidatorEquivalent()
    {
        RuleFor(_ => _.Id)
            .NotEqual(Guid.Empty);
        RuleFor(_ => _.FirstName)
            .NotEmpty();
        RuleFor(_ => _.MiddleName)
            .SetValidator(new NotWhiteSpaceValidator<Person>());
        RuleFor(_ => _.FamilyName)
            .NotEmpty();
        RuleFor(_ => _.Dob)
            .NotEqual(DateTimeOffset.MinValue);
    }
}

snippet source | anchor

Shared Rules

Given the following models:

public interface IDbRecord
{
    public byte[] RowVersion { get; }
    public Guid Id { get; }
}

public class Person :
    IDbRecord
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public byte[] RowVersion { get; set; }
}

snippet source | anchor

It is desirable to have the rules for IDbRecord defined separately, and not need to duplicate them for every implementing class. This can be done using shares rules.

Configure any shared rules at startup:

[ModuleInitializer]
public static void Init() =>
    ValidatorExtensions.SharedValidatorFor<IDbRecord>()
        .RuleFor(record => record.RowVersion)
        .Must(rowVersion => rowVersion?.Length == 8)
        .WithMessage("RowVersion must be 8 bytes");

snippet source | anchor

The PersonValidator used only the standard rules, so needs no constructor.

class PersonValidator :
    ExtendedValidator<Person>;

snippet source | anchor

The above is equivalent to:

class PersonValidatorEquivalent :
    AbstractValidator<Person>
{
    public PersonValidatorEquivalent()
    {
        RuleFor(_ => _.Id)
            .NotEqual(Guid.Empty);
        RuleFor(_ => _.Name)
            .NotEmpty();
        RuleFor(_ => _.RowVersion)
            .NotNull()
            .Must(rowVersion => rowVersion?.Length == 8)
            .WithMessage("RowVersion must be 8 bytes");
    }
}

snippet source | anchor

Icon

Pointed Star designed by Eliricon from The Noun Project.

About

Extends FluentValidation with some more opinionated rules.

License:MIT License


Languages

Language:C# 100.0%