CheckValidators 1.1.8

There is a newer version of this package available.
See the version list below for details.
dotnet add package CheckValidators --version 1.1.8                
NuGet\Install-Package CheckValidators -Version 1.1.8                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CheckValidators" Version="1.1.8" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add CheckValidators --version 1.1.8                
#r "nuget: CheckValidators, 1.1.8"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install CheckValidators as a Cake Addin
#addin nuget:?package=CheckValidators&version=1.1.8

// Install CheckValidators as a Cake Tool
#tool nuget:?package=CheckValidators&version=1.1.8                

Check Validators (.NET)

Author: Ryan Kueter
Updated: June, 2022

About

Check Validators is a free .NET library, available from the NuGet Package Manager, that provides the most flexible, simple, and powerful way to validate and guard your data. It provides a variety of user-defined validators that allow you to use LINQ to validate your data, in addition to over 100 built-in validators. And it allows you to write your own custom validators and use them in your project without modifying this library.

Targets:

  • .NET 6

Introduction

Each "Check" contains validation rules that can be chained together using method extension syntax (builder pattern).

// Validating a date
try
{
    DateTime datetime = DateTime.Now;
    new Check<DateTime>(datetime)
        .IfNull()
        .IfDefault()
        .IfNotUtcTime()
        .IfNot(date => date == DateTime.Now.AddDays(-1), "The date was not yesterday!")
        .ThrowErrors();
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

Output:
Errors: 1) The datetime format is not Utc, 2) The date was not yesterday!, in Program.cs:line 48. (Parameter 'datetime <DateTime>')

Or you can turn off verbose logging by using ThrowErrors(false):

Errors: 1) The datetime format is not Utc, 2) The date was not yesterday!.

A Realistic Example

One of the features that makes this library so powerful is not just the ability to use LINQ to validate class members and collections, but you can also nest Check validators inside of those validation rules. This can allow you to more easily validate the contents of lists and other types of collections.

In the following examples, the following checks are being used inside of user-defined validation rules.

  • Check<string>(request.Email).IfNotEmail()
  • Check<DateTime>(request.TimeStamp).IfNull().IfDefault().IfNotUtcTime()
  • Check<string>(request.User).IfEquals("String Comparison Example", StringComparison.InvariantCulture)
  • Check<string>(request.User).IfNotMatches("^letterstomatch", System.Text.RegularExpressions.RegexOptions.None)
//Validating a service request
try
{
    var request = new MyServiceRequest();
    new Check<MyServiceRequest>(request)
        .IfNull()
        /*
         * Check if the email is valid.
         */
        .If(request => request.Email == null, "The email address was null!")
        .AndIf(request => new Check<string>(request.Email).IfNotEmail().HasErrors())
        /*
         * Check if the timestamp is valid
         */
        .If(request => request.TimeStamp == default, "The timestamp was not set!")
        .AndIf(request => new Check<DateTime>(request.TimeStamp).IfNull().IfDefault().IfNotUtcTime().HasErrors(), "An error occured with the timestamp")
        .If(request => request.Id == 0)
        .If(request => request.People is null || request.People.Count == 0)
        .AndIf(request => request.People.Where(person => new Check<string>(person.Email).IfNull().IfNotEmail().HasErrors()).Any(), "A person in the list of people did not have a valid email address")
        .AndIfNot(p => p.People.Where(x => x.Id > 0).Any())
        .If(request => new Check<string>(request.User).IfEquals("String Comparison Example", StringComparison.InvariantCulture).HasErrors())
        .If(request => new Check<string>(request.User).IfNotMatches("^rya", System.Text.RegularExpressions.RegexOptions.IgnoreCase).HasErrors())
        .IfNot(request => request.Count > 20 && request.Count < 300)
        .ThrowErrors();
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

Output:
Errors: 1) The email address was null!, 2) The timestamp was not set!, 3) A person in the list of people did not have a valid email address, 4) IfNot(request => request.Count > 20 && request.Count < 300), in Program.cs:line 24. (Parameter 'request <MyServiceRequest>')

Validations

Each validation rule provides a default error message that includes specifics about the causes of the error. With the user-defined validation rules, the errors display the logic used in the validation rule, or you can specify your own custom error.

Utilities

Check validators comes with a number of utilities that can make validating data easier and more flexible. These utilities can be used inside of user-defined validation rules to create more powerful set of validations.

using CheckValidators;

string? data = "Adam Smith";

var c = new Check<string>(data)
    .IfNull()
    .IfEmptyOrWhitespace()
    .IfLengthEquals(10)
    .IfNot(s => s.Contains("keyword"), "The string does not contain the keyword.");
HasErrors()

If you want to determine if any validation errors occured, you could call this to give you a boolean result.

GetErrors()

You can also retrieve each individual error for your own error handling.

// Getting errors
if (c.HasErrors())
{
    foreach (var s in c.GetErrors())
    {
        Console.WriteLine(s);
    }
}
IsValid()

IsValid() returns a boolean true or false depending on whether all the rules passed validation. If they do, it will return true. If one failed, it will return false. This can be useful for validation scenarios where you don't want to throw an error.

ThrowErrors()

ThrowErrors() will throw all the errors in a formatted error message. The default setting for errors is verbose, which displays the file name, line number, and parameter. But if you want a simpler error for display purposes, you can switch it off by supplying a false parameter: ThrowErrors(false).

// Throwing errors
if (!c.IsValid())
{
    try
    {
        c.ThrowErrors();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

The expression above, for example, will throw the following errors:

Errors: 1) String length should not equal 10 characters, 2) The string does not contain the keyword, in Program.cs:line 5. (Parameter 'data <String>')

User-Defined Validation Rules

User-defined validation rules allow you to use LINQ to validate your data. When writing user-defined validation rules, you typically want to write your own error message. If you don't, the expression that fails will be recorded.

If and IfNot

If and IfNot conditions allow you to use LINQ to query objects and lists in the object. If you use the standard default error message, it will include the query you are using in your message, in addition to the name and type of object you are checking.

If(user => user.User is null)
If(user => user.User is null, "Custom error message.")
// Default Error: If({expression})

IfNot(user => user.User is null)
IfNot(user => user.User is null, "Custom error message.")
// Default Error: IfNot({expression})

AndIf and AndIfNot

AndIf and AndIfNot are similar to If and IfNot conditions, except that they only execute when the previous If or IfNot condition is valid. This provides better performance, and prevents unnecessary code execution and unnecessary errors. An example may include checking a child value of a value that was previously determined to be null. For example, if a list of people is null, you don't want to check that list for other validation errors because they will all fail. So, in that event, all of the following AndIf and AndIfNot statements are skipped. This can be reset by defining a new If or IfNot validation rule.

AndIf(user => user.User is null)
AndIf(user => user.User is null, "Custom error message.")
// Default Error: AndIf({expression})

AndIfNot(user => user.User is null)
AndIfNot(user => user.User is null, "Custom error message.")
// Default Error: AndIfNot({expression})

OrIf and OrIfNot

OrIf and OrIfNot are the opposite of AndIf and AndIfNot and only execute if a previous If rule fails validation. This can be reset by defining a new If or IfNot validation rule.

OrIf(user => user.User is null)
OrIf(user => user.User is null, "Custom error message.")
// Default Error: OrIf({expression})

OrIfNot(user => user.User is null)
OrIfNot(user => user.User is null, "Custom error message.")
// Default Error: OrIfNot({expression})

Built-in Validations

Check validators also include a large number of built-in validation rules. Each of these rules has their own predefined errors that will provide the parameters you are supplying in the error messages.

General

If you want to throw an error that the object you are checking is null, then you need to include IfNull(). If the object you are checking is null, none of the other validations will be checked. So, this is a good one to include.

IfNull()
// Error: The value is null

IfNotNull()  
// Error: The value is not null

String

IfNotDate()
// Error: String {data.Value} is not a date

IfNotInteger()
// Error: String {data.Value} is not an integer

IfNotEmail()
IfNotEmail(RegexPattern, System.Text.RegularExpressions.RegexOptions.None)
// Error: String '{data.Value}' is not an email address

IfNotURL()
IfNotURL(RegexPattern, System.Text.RegularExpressions.RegexOptions.None)
// Error: String {data.Value} is not a URL

IfNotValidPassword()
IfNotValidPassword(8, 2, 2, 2, 2)
IfNotValidPassword(MinLength, Uppercase, Lowercase, Numbers, SpecialCharacters)

// Possible errors:
The password does not contain:
A minimum of {minLength} characters.
A minimum of {numUpper} upper-case characters.
A minimum of {numLower} lower-case characters.
A minimum of {numNumbers} numbers.
A minimum of {numSpecial} special characters.

IfMatches(@"\b[R]\w+")
IfMatches(@"\b[R]\w+", System.Text.RegularExpressions.RegexOptions.None)
// Error: String should not match the regular expressions pattern '{pattern}'

IfNotMatches(@"\b[R]\w+")
IfNotMatches(@"\b[R]\w+", System.Text.RegularExpressions.RegexOptions.None)
// Error: String should match the regular expressions pattern '{pattern}'

IfEmpty()
// Error: String is empty

IfWhitespace()
// Error: String is whitepace

IfEmptyOrWhitespace()
// Error: String is empty or whitespace

IfEquals("String")
IfEquals("String", StringComparison.InvariantCulture)
// Error: String should not be equal to '{compareString}' [StringComparison: '{compareType}']

IfNotEquals("String")
IfNotEquals("String", StringComparison.InvariantCulture)
// Error: String should be equal to '{compareString}' [StringComparison: '{compareType}']

IfLengthGreaterThan(5)
// Error: String has exceeded the character limit of {length} characters

IfLengthLessThan(5)
// Error: String does not meet the minimum character length of {length} characters

IfLengthEquals(5)
// Error: String length should not equal {length} characters

IfNotLengthEquals(5)
// Error: String length should equal {length} characters

IfEndsWith("abc")
IfEndsWith("abc", StringComparison.InvariantCulture)
// Error: String should not end with '{ending}' [StringComparison: '{compareType}']

IfNotEndsWith("abc")
IfNotEndsWith("abc", StringComparison.InvariantCulture)
// Error: String does not end with '{ending}' [StringComparison: '{compareType}']

IfStartsWith("abc")
IfStartsWith("abc", StringComparison.InvariantCulture)
// Error: String should not start with '{beginning}' [StringComparison: '{compareType}']

IfNotStartsWith("abc")
IfNotStartsWith("abc", StringComparison.InvariantCulture)
// Error: String does not start with '{beginning}' [StringComparison: '{compareType}']

IfContains("abc")
IfContains("abc", StringComparison.InvariantCulture)
// Error: String should not contain '{compare}' [StringComparison: '{compareType}']

IfNotContains("abc")
IfNotContains("abc", StringComparison.InvariantCulture)
// Error: String should contain '{compare}' [StringComparison: '{compareType}']

Arrays

IfEmpty()
// Error: The array is empty

IfNotEmpty()
// Error: The array is not empty

IfCount(5)
// Error: The item count should not be {count}

IfNotCount(5)
// Error: The item count is not {count}

IfCountGreaterThan(5)
// Error: The item count is greater than {count}

IfCountLessThan(5)
// Error: The item count is less than {count}

List

IfEmpty()
// Error: The list is empty

IfNotEmpty()
// Error: The list is not empty

IfCount(5)
// Error: The item count should not be {count}

IfNotCount(5)
// Error: The item count is not {count}

IfCountGreaterThan(5)
// Error: The item count is greater than {count}

IfCountLessThan(5)
// Error: The item count is less than {count}

Dictionary

IfEmpty()
// Error: Dictionary is empty

IfNotEmpty()
// Error: The Dictionary is not empty

IfCount(5)
// Error: The item count should not be {count}

IfNotCount(5)
// Error: The item count is not {count}

IfCountGreaterThan(5)
// Error: The item count is greater than {count}

IfCountLessThan(5)
// Error: The item count is less than {count}

DateTime

IfDaysOlderThan(5)
// Error: The datetime '{value}' is older than {days} days

IfMinutesOlderThan(60)
// Error: The datetime '{value}' is older than {minutes} minutes

IfSecondsOlderThan(60)
// Error: The datetime '{value}' is older than {seconds} seconds

IfMillisecondsOlderThan(6000)
// Error: The datetime '{value}' is older than {milliseconds} milliseconds

IfEarlierThan(dateTime)
// Error: The datetime '{value}' is earlier than '{dateTime}'

IfLaterThan(dateTime)
// Error: The datetime '{value}' is later than '{dateTime}'

IfEqual(dateTime)
// Error: The datetime '{value}' is equal to '{dateTime}'

IfNotEqual(dateTime)
// Error: The datetime '{value}' is not equal to '{dateTime}'

IfBetween(startTime, endTime)
// Error: The datetime '{value}' is between '{startTime}' and '{endTime}'

IfNotBetween(startTime, endTime)
// Error: The datetime '{value}' is not between '{startTime}' and '{endTime}'

IfUtcTime()
// Error: The datetime format is Utc

IfNotUtcTime()
// Error: The datetime format is not Utc

IfLocalTime()
// Error: The datetime format is local

IfNotLocalTime()
// Error: The datetime format is not local

IfUnspecifiedTimeFormat()
// Error: The datetime format is unspecified

IfDayLightSavingsTime()
// Error: The datetime '{value}' is on daylight savings time

IfNotDayLightSavingsTime()
// Error: The datetime '{value}' is not on daylight savings time

IfDefault()
// Error: The datetime is set to the default value

IfNotDefault()
// Error: The datetime is not set to the default value

IfSunday()
// Error: The day of the week should not be Sunday

IfNotSunday()
// Error: The day of the week should be Sunday

IfMonday()
// Error: The day of the week should not be Monday

IfNotMonday()
// Error: The day of the week should be Monday

IfTuesday()
// Error: The day of the week should not be Tuesday

IfNotTuesday()
// Error: The day of the week should be Tuesday

IfWednesday()
// Error: The day of the week should not be Wednesday

IfNotWednesday()
// Error: The day of the week should be Wednesday

IfThursday()
// Error: The day of the week should not be Thursday

IfNotThursday()
// Error: The day of the week should be Thursday

IfFriday()
// Error: The day of the week should not be Friday

IfNotFriday()
// Error: The day of the week should be Friday

IfSaturday()
// Error: The day of the week should not be Saturday

IfNotSaturday()
// Error: The day of the week should be Saturday

TimeOnly

IfDefault()
// Error: The timeonly is set to the default value of {value}

IfNotDefault()
// Error: The timeonly '{value}' is not set to the default value

IfMinValue()
// Error: The timeonly is set to the minimum value of {value}

IfNotMinValue()
// Error: The timeonly '{value}' is not set to the minimum value of {TimeOnly.MinValue}

IfMaxValue()
// Error: The timeonly is set to the maximum value of {value}

IfNotMaxValue()
// Error: The timeonly '{value}' is not set to the maximum value of {TimeOnly.MaxValue}

IfBetween(startTime, endTime)
// Error: The timeonly is between '{startTime}' and '{endTime}'

IfNotBetween(startTime, endTime)
// Error: The timeonly is not between '{startTime}' and '{endTime}'

DateOnly

IfDefault()
// Error: The dateonly is set to the default value of {value}

IfNotDefault()
// Error: The dateonly '{value}' is not set to the default value

IfMinValue()
// Error: The dateonly is set to the minimum value of {value}

IfNotMinValue()
// Error: The dateonly '{value}' is not set to the minimum value of {DateOnly.MinValue}

IfMaxValue()
// Error: The dateonly is set to the maximum value of {value}

IfNotMaxValue()
// Error: The dateonly '{value}' is not set to the maximum value of {DateOnly.MaxValue}

Double

IfNegative()
// Error: The double is negative

IfPositive()
// Error: The double is positive

IfZero()
// Error: The double is zero

IfNotZero()
// Error: The double is not zero

IfGreaterThan(5)
// Error: The double is greater than {value}

IfLessThan(5)
// Error: The double is less than {value}

IfEquals(5)
// Error: The double should not be {value}

IfNotEquals(5)
// Error: The double should be {value}

Float

IfBetween(startValue, endValue)
// Error: The float '{value}' is between '{startValue}' and '{endValue}'

IfNotBetween(startValue, endValue)
// Error: The float '{value}' is not between '{startValue}' and '{endValue}'

IfNegative()
// Error: The float is negative

IfPositive()
// Error: The float is positive

IfZero()
// Error: The float is zero

IfNotZero()
// Error: The float is not zero

IfGreaterThan(5)
// Error: The float is greater than {value}

IfLessThan(5)
// Error: The float is less than {value}

IfEquals(5)
// Error: The float should not be {value}

IfNotEquals(5)
// Error: The float should be {value}

Short (Int16)

IfBetween(startValue, endValue)
// Error: The number '{value}' is between '{startValue}' and '{endValue}'

IfNotBetween(startValue, endValue)
// Error: The number '{value}' is not between '{startValue}' and '{endValue}'

IfNegative()
// Error: The number is negative

IfPositive()
// Error: The number is positive

IfZero()
// Error: The number is zero

IfNotZero()
// Error: The number is not zero

IfGreaterThan(5)
// Error: The number is greater than {value}

IfLessThan(5)
// Error: The number is less than {value}

IfEquals(5)
// Error: The number should not be {value}

IfNotEquals(5)
// Error: The number should be {value}

Int (Int32)

IfBetween(startValue, endValue)
// Error: The number '{value}' is between '{startValue}' and '{endValue}'

IfNotBetween(startValue, endValue)
// Error: The number '{value}' is not between '{startValue}' and '{endValue}'

IfNegative()
// Error: The number is negative

IfPositive()
// Error: The number is positive

IfZero()
// Error: The number is zero

IfNotZero()
// Error: The number is not zero

IfGreaterThan(5)
// Error: The number is greater than {value}

IfLessThan(5)
// Error: The number is less than {value}

IfEquals(5)
// Error: The number should not be {value}

IfNotEquals(5)
// Error: The number should be {value}

Long (Int64)

IfBetween(startValue, endValue)
// Error: The number '{value}' is between '{startValue}' and '{endValue}'

IfNotBetween(startValue, endValue)
// Error: The number '{value}' is not between '{startValue}' and '{endValue}'

IfNegative()
// Error: The number is negative

IfPositive()
// Error: The number is positive

IfZero()
// Error: The number is zero

IfNotZero()
// Error: The number is not zero

IfGreaterThan(5)
// Error: The number is greater than {value}

IfLessThan(5)
// Error: The number is less than {value}

IfEquals(5)
// Error: The number should not be {value}

IfNotEquals(5)
// Error: The number should be {value}

Decimal

IfBetween(startValue, endValue)
// Error: The decimal '{value}' is between '{startValue}' and '{endValue}'

IfNotBetween(startValue, endValue)
// Error: The decimal '{value}' is not between '{startValue}' and '{endValue}'

IfNegative()
// Error: The decimal is negative

IfPositive()
// Error: The decimal is positive

IfZero()
// Error: The decimal is zero

IfNotZero()
// Error: The decimal is not zero

IfGreaterThan(5)
// Error: The decimal is greater than {value}

IfLessThan(5)
// Error: The decimal is less than {value}

IfEquals(5)
// Error: The decimal should not be {value}

IfNotEquals(5)
// Error: The decimal should be {value}

Uri

IfScheme()
// Error: Uri scheme should not be '{scheme}'

IfNotScheme()
// Error: Uri scheme is not '{scheme}'

IfAbsoluteUri()
// Error: Uri is absolute, consider changing it to relative

IfRelativeUri()
// Error: Uri is relative, consider changing it to absolute

IfUriPort(80)
// Error: Uri port should not be {port}

IfNotUriPort(80)
// Error: Uri port should be {port}

IfFile()
// Error: Uri is a file path

IfNotFile()
// Error: Uri is not a file

IfUnc()
// Error: Uri is a UNC path

IfNotUnc()
// Error: Uri is not a UNC path

IfLoopback()
// Error: Uri is the loopback address

IfNotLoopback()
// Error: Uri is not the loopback address

Extensibility

You can create your own custom extension methods anywhere in your project to add custom validators that are specific to your needs, similar to the following:

public static partial class CheckValidatorsExtensions
{
    public static Check<List<T>?> IfEmpty<T>(this Check<List<T>?> data)
    {
        if (data.InvalidModel()) { return data; }
        try
        {
            if (!data.Value.Any())
            {
                data.ThrowError("The list is empty.");
            }
        }
        catch { }
        return data;
    }
}

Contributions

This project is being developed for free by me, Ryan Kueter, in my spare time. So, if you would like to contribute, please submit your ideas on the Github project page.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net6.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.1.32 142 3/30/2024
1.1.31 118 3/15/2024
1.1.30 253 11/25/2023
1.1.29 155 11/25/2023
1.1.28 177 10/18/2023
1.1.27 179 9/12/2023
1.1.26 173 9/11/2023
1.1.25 162 9/10/2023
1.1.24 201 9/9/2023
1.1.23 177 9/9/2023
1.1.22 181 9/9/2023
1.1.21 179 9/9/2023
1.1.20 180 9/9/2023
1.1.19 408 11/26/2022
1.1.18 451 9/30/2022
1.1.17 474 9/12/2022
1.1.16 443 8/15/2022
1.1.15 466 6/10/2022
1.1.14 460 6/10/2022
1.1.13 462 6/10/2022
1.1.12 454 6/7/2022
1.1.10 457 6/6/2022
1.1.9 472 6/6/2022
1.1.8 458 6/6/2022
1.1.7 439 6/6/2022
1.1.6 464 6/5/2022
1.1.5 446 6/5/2022
1.1.4 450 6/3/2022
1.1.3 464 6/3/2022
1.1.2 466 6/2/2022
1.1.0 492 6/2/2022
1.0.17 495 5/3/2022
1.0.16 490 5/2/2022
1.0.15 503 5/2/2022
1.0.14 488 2/22/2022
1.0.13 467 2/22/2022

Added a large number of numeric and datetime built-in validators, and added built-in validators for TimeOnly and DateOnly. The previous version included a large number new features, inaddition to a few minor breaking changes. The IfElse methods deprecated in previous versions were finally removed. The ability to add custom errors were kept for the user-defined methods, like If, IfNot, AndIf, AndIfNot, OrIf, and OrIfNot, but were removed from the built-in methods in favor of readability, simplicity, extensibility, and more accurate and descriptive errors.