An open API service indexing awesome lists of open source software.

https://github.com/simoncropp/extendedfluentvalidation

Extends FluentValidation with some more opinionated rules.
https://github.com/simoncropp/extendedfluentvalidation

Last synced: about 1 year ago
JSON representation

Extends FluentValidation with some more opinionated rules.

Awesome Lists containing this project

README

          

# ExtendedFluentValidation

[![Build status](https://ci.appveyor.com/api/projects/status/3lr9er83fo8mij5i?svg=true)](https://ci.appveyor.com/project/SimonCropp/ExtendedFluentValidation)
[![NuGet Status](https://img.shields.io/nuget/v/ExtendedFluentValidation.svg)](https://www.nuget.org/packages/ExtendedFluentValidation/)

Extends [FluentValidation](https://fluentvalidation.net/) with some more opinionated rules and extensions.

**See [Milestones](../../milestones?state=closed) 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. The logic being: if the absence of text is valid, then make the member nullable. This helps since nullable is a first class strong type feature, where "string is empty or only white-space" is a runtime check.

### Guids

Guids cannot be `Guid.Empty`.

### Lists/Collections

Lists and Collection cannot be empty if `ValidatorConventions.ValidateEmptyLists()` is called in a module initializer. The logic being: if the absence of any values is valid, then make the member nullable. This helps since nullable is a first class strong type feature, where "list contains no values" is a runtime check.

## Usage

There are two ways of applying the extended rules.

### ExtendedValidator

Using a base class `ExtendedValidator`:


```cs
class PersonValidatorFromBase :
ExtendedValidator
{
public PersonValidatorFromBase()
{
//TODO: add any extra rules
}
}
```
snippet source | anchor

### AddExtendedRules

Using an extension method `AddExtendedRules`:


```cs
class PersonValidatorNonBase :
AbstractValidator
{
public PersonValidatorNonBase() =>
this.AddExtendedRules();
//TODO: add any extra rules
}
```
snippet source | anchor

### Equivalent

The above are equivalent to:


```cs
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


```cs
class PersonValidatorEquivalent :
AbstractValidator
{
public PersonValidatorEquivalent()
{
RuleFor(_ => _.Id)
.NotEqual(Guid.Empty);
RuleFor(_ => _.FirstName)
.NotEmpty();
RuleFor(_ => _.MiddleName)
.SetValidator(new NotWhiteSpaceValidator());
RuleFor(_ => _.FamilyName)
.NotEmpty();
RuleFor(_ => _.Dob)
.NotEqual(DateTimeOffset.MinValue);
}
}
```
snippet source | anchor

## Shared Rules

Given the following models:


```cs
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:


```cs
[ModuleInitializer]
public static void Init() =>
ValidatorConventions.ValidatorFor()
.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.


```cs
class PersonValidator :
ExtendedValidator;
```
snippet source | anchor

The above is equivalent to:


```cs
class PersonValidatorEquivalent :
AbstractValidator
{
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](https://thenounproject.com/term/pointed+star/802333/) designed by [Eliricon](https://thenounproject.com/mordarius/) from [The Noun Project](https://thenounproject.com).