Phema.Validation 3.1.9

C# strongly-typed validation library for .NET

There is a newer version of this package available.
See the version list below for details.
Install-Package Phema.Validation -Version 3.1.9
dotnet add package Phema.Validation --version 3.1.9
<PackageReference Include="Phema.Validation" Version="3.1.9" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Phema.Validation --version 3.1.9
The NuGet Team does not provide support for this client. Please contact its maintainers for support.

Phema.Validation

Build Status
Nuget
Nuget

A simple, lightweight, fluent and extensible validation library for .NET Core

Installation

$> dotnet add package Phema.Validation

Core concepts

  • IValidationContext - Scoped service to store all validation details
  • IValidationCondition - Contains a validation checks (e.g. Is(() =&gt; ...))
  • IValidationDetail - When IValidationCondition is not valid adds to IValidationContext.ValidationDetails
  • ValidationSeverity - Validation error level, used in IValidationContext.ValidationSeverity and IValidationDetail.ValidationSeverity
  • IValidationScope - Is a nested validation context with validation path and severity override

Usage (ASP.NET Core, HostedService examples)

// Add `IValidationContext` as scoped service
services.AddValidation(options => ...);

// Get or inject
var validationContext = serviceProvider.GetRequiredService<IValidationContext>();

// Validation key will be `Name` using default validation part provider
validationContext.When(person, p => p.Name)
  .Is(name => name == null)
  .AddValidationDetail("Name must be set");

// Validation key will be `Address.Locations[0].Latitude` using default validation part provider
validationContext.When(person, p => p.Address.Locations[0].Latitude)
  .Is(latitude => ...custom check...)
  .AddValidationDetail("Some custom check failed");

Validation conditions

  • Monads are not composable, so Is and IsNot, IsNull and IsNotNull... duplication
// Check for Phema.Validation.Conditions namespace
validationContext.When(person, p => p.Name)
  .IsNullOrWhitespace()
  .AddValidationDetail("Name must be set");

// Use multiple conditions (joined with AND)
validationContext.When(person, p => p.Name)
  .IsNotNull()
  // AND
  .HasLengthGreater(20)
  // .IsNull()
  // .IsEqual()
  // .IsNotUrl()
  // .IsNotEmail()
  // .IsMatch(regex)
  .AddValidationDetail("Name should be less than 20");

// DateTime conditions
validationContext.When(task, t => t.DueDate)
  .IsNotUtc()
  .AddValidationDetail("Due date must be in Utc");

// Type checks
validationContext.When(person, p => p.Car)
  .IsType<Ferrari>(typed => typed.Is(ferarriCar => ...Some Ferrari specific checks...))
  .AddValidationDetail("You have Ferrari car, but ...");

Validation details

var validationDetails = validationContext
  .When(person, p => p.Age)
  // Validation check is failed, validation condition is valid
  .Is(() => false)
  .AddValidationDetail("Age must be set");

// Use deconstruction
var (key, message) = validationContext
  .When(person, p => p.Age)
  .IsNull()
  .AddValidationDetail("Age must be set");

// More deconstruction
var (key, message, isValid) = validationContext
  .When(person, p => p.Age)
  .IsNull()
  .AddValidationDetail("Age must be set");

// Even more deconstruction!
var (key, message, isValid, severity) = validationContext
  .When(person, p => p.Age)
  .IsNull()
  .AddValidationDetail("Age must be set");

Check validation

// Throw exception when details severity greater than ValidationContext.ValidationSeverity
validationContext.When(person, p => p.Address)
  .IsNull()
  .AddValidationFatal("Address is not presented!!!"); // If invalid throw ValidationConditionException

// Check if context is valid
validationContext.IsValid();
validationContext.EnsureIsValid(); // If invalid throw ValidationContextException

// Check concrete validation details
validationContext.IsValid(person, p => p.Age);
validationContext.IsNotValid(person, p => p.Age);
validationContext.EnsureIsValid(person, p => p.Age);

Compose and reuse validation rules with extensions

  • Call is allocation free
  • Static checks
// Extensions
public static void ValidateCustomer(this IValidationContext validationContext, Customer customer)
{
  // Some checks
}

validationContext.ValidateCustomer(customer);
  • Write your own middleware or validation components/validators on top of IValidationContext

Validation part resolvers

  • ValidationPartResolver is a delegate, trying to get string valdiation part from MemberInfo
  • Use built in resolvers with ValidationPartResolvers static class: Default, DataMember, PascalCase, CamelCase
// Configure DataMember validation part resolver
services.AddValidation(options =>
  options.ValidationPartResolver = ValidationPartResolvers.DataMember);

// Override validation parts with `DataMemberAttribute`
[DataMember(Name = "name")]
public string Name { get; set; }

Validation scopes

  • Use scopes when you need to have:
    • Same nested validation path multiple times
    • Empty validation details collection (syncing with parent context/scope)
    • ValidationSeverity override
// Validation key will be `Child.*ValidationPart*`
ValidateChild(validationContext.CreateScope(parent, p => p.Child))

// Validation key will be `Address.Locations[0].*ValidationPart*`
ValidateLocation(validationContext.CreateScope(person, p => p.Address.Locations[0]))

// Override local scope ValidationSeverity
using (var scope = validationContext.CreateScope(person, p => p.Address, ValidationSeverity.Warning))
{
  // Some scope validation checks syncing with validationContext
}

High performance with non-expression constructions

validationContext.When("key", value)
  .IsNull()
  .AddValidationDetail("Value is null");

validationContext.CreateScope("key", ValidationSeverity.Warning);

validationContext.IsValid("key");
validationContext.IsNotValid("key");
validationContext.EnsureIsValid("key");

Phema.Validation

Build Status
Nuget
Nuget

A simple, lightweight, fluent and extensible validation library for .NET Core

Installation

$> dotnet add package Phema.Validation

Core concepts

  • IValidationContext - Scoped service to store all validation details
  • IValidationCondition - Contains a validation checks (e.g. Is(() =&gt; ...))
  • IValidationDetail - When IValidationCondition is not valid adds to IValidationContext.ValidationDetails
  • ValidationSeverity - Validation error level, used in IValidationContext.ValidationSeverity and IValidationDetail.ValidationSeverity
  • IValidationScope - Is a nested validation context with validation path and severity override

Usage (ASP.NET Core, HostedService examples)

// Add `IValidationContext` as scoped service
services.AddValidation(options => ...);

// Get or inject
var validationContext = serviceProvider.GetRequiredService<IValidationContext>();

// Validation key will be `Name` using default validation part provider
validationContext.When(person, p => p.Name)
  .Is(name => name == null)
  .AddValidationDetail("Name must be set");

// Validation key will be `Address.Locations[0].Latitude` using default validation part provider
validationContext.When(person, p => p.Address.Locations[0].Latitude)
  .Is(latitude => ...custom check...)
  .AddValidationDetail("Some custom check failed");

Validation conditions

  • Monads are not composable, so Is and IsNot, IsNull and IsNotNull... duplication
// Check for Phema.Validation.Conditions namespace
validationContext.When(person, p => p.Name)
  .IsNullOrWhitespace()
  .AddValidationDetail("Name must be set");

// Use multiple conditions (joined with AND)
validationContext.When(person, p => p.Name)
  .IsNotNull()
  // AND
  .HasLengthGreater(20)
  // .IsNull()
  // .IsEqual()
  // .IsNotUrl()
  // .IsNotEmail()
  // .IsMatch(regex)
  .AddValidationDetail("Name should be less than 20");

// DateTime conditions
validationContext.When(task, t => t.DueDate)
  .IsNotUtc()
  .AddValidationDetail("Due date must be in Utc");

// Type checks
validationContext.When(person, p => p.Car)
  .IsType<Ferrari>(typed => typed.Is(ferarriCar => ...Some Ferrari specific checks...))
  .AddValidationDetail("You have Ferrari car, but ...");

Validation details

var validationDetails = validationContext
  .When(person, p => p.Age)
  // Validation check is failed, validation condition is valid
  .Is(() => false)
  .AddValidationDetail("Age must be set");

// Use deconstruction
var (key, message) = validationContext
  .When(person, p => p.Age)
  .IsNull()
  .AddValidationDetail("Age must be set");

// More deconstruction
var (key, message, isValid) = validationContext
  .When(person, p => p.Age)
  .IsNull()
  .AddValidationDetail("Age must be set");

// Even more deconstruction!
var (key, message, isValid, severity) = validationContext
  .When(person, p => p.Age)
  .IsNull()
  .AddValidationDetail("Age must be set");

Check validation

// Throw exception when details severity greater than ValidationContext.ValidationSeverity
validationContext.When(person, p => p.Address)
  .IsNull()
  .AddValidationFatal("Address is not presented!!!"); // If invalid throw ValidationConditionException

// Check if context is valid
validationContext.IsValid();
validationContext.EnsureIsValid(); // If invalid throw ValidationContextException

// Check concrete validation details
validationContext.IsValid(person, p => p.Age);
validationContext.IsNotValid(person, p => p.Age);
validationContext.EnsureIsValid(person, p => p.Age);

Compose and reuse validation rules with extensions

  • Call is allocation free
  • Static checks
// Extensions
public static void ValidateCustomer(this IValidationContext validationContext, Customer customer)
{
  // Some checks
}

validationContext.ValidateCustomer(customer);
  • Write your own middleware or validation components/validators on top of IValidationContext

Validation part resolvers

  • ValidationPartResolver is a delegate, trying to get string valdiation part from MemberInfo
  • Use built in resolvers with ValidationPartResolvers static class: Default, DataMember, PascalCase, CamelCase
// Configure DataMember validation part resolver
services.AddValidation(options =>
  options.ValidationPartResolver = ValidationPartResolvers.DataMember);

// Override validation parts with `DataMemberAttribute`
[DataMember(Name = "name")]
public string Name { get; set; }

Validation scopes

  • Use scopes when you need to have:
    • Same nested validation path multiple times
    • Empty validation details collection (syncing with parent context/scope)
    • ValidationSeverity override
// Validation key will be `Child.*ValidationPart*`
ValidateChild(validationContext.CreateScope(parent, p => p.Child))

// Validation key will be `Address.Locations[0].*ValidationPart*`
ValidateLocation(validationContext.CreateScope(person, p => p.Address.Locations[0]))

// Override local scope ValidationSeverity
using (var scope = validationContext.CreateScope(person, p => p.Address, ValidationSeverity.Warning))
{
  // Some scope validation checks syncing with validationContext
}

High performance with non-expression constructions

validationContext.When("key", value)
  .IsNull()
  .AddValidationDetail("Value is null");

validationContext.CreateScope("key", ValidationSeverity.Warning);

validationContext.IsValid("key");
validationContext.IsNotValid("key");
validationContext.EnsureIsValid("key");

This package is not used by any popular GitHub repositories.

Version History

Version Downloads Last updated
3.1.10 39 10/12/2019
3.1.9 90 9/27/2019
3.1.8 80 9/25/2019
3.1.7 91 9/15/2019
3.1.6 48 9/8/2019
3.1.5 38 9/7/2019
3.1.4 37 9/7/2019
3.1.3 58 9/7/2019
3.1.2 36 8/25/2019
3.1.1 35 8/24/2019
3.1.0 36 8/23/2019
3.0.9 36 8/23/2019
3.0.8 37 8/22/2019
3.0.7 38 8/16/2019
3.0.6 39 7/30/2019
3.0.5 37 7/29/2019
3.0.4 37 7/29/2019
3.0.3 36 7/28/2019
3.0.2 36 7/21/2019