Phema.Validation 3.0.6

There is a newer version of this package available.
See the version list below for details.
dotnet add package Phema.Validation --version 3.0.6                
NuGet\Install-Package Phema.Validation -Version 3.0.6                
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="Phema.Validation" Version="3.0.6" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Phema.Validation --version 3.0.6                
#r "nuget: Phema.Validation, 3.0.6"                
#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 Phema.Validation as a Cake Addin
#addin nuget:?package=Phema.Validation&version=3.0.6

// Install Phema.Validation as a Cake Tool
#tool nuget:?package=Phema.Validation&version=3.0.6                

Phema.Validation

Build Status Nuget

C# strongly typed expression-based validation library for .NET

Installation

$> dotnet add package Phema.Validation

Usage (example)

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

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

// Validation key will be `Name`
validationContext.When(person, p => p.Name)
  .Is(name => name == null)
  .AddError("Name must be set");

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

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

Validation conditions

// Check for Phema.Validation.Conditions namespace
validationContext.When(person, p => p.Name)
  .IsNullOrWhitespace()
  .AddError("Name must be set");

// Use multiple conditions (joined with AND)
validationContext.When(person, p => p.Name)
  .IsNotNull()
  .HasLengthGreater(20)
  // .IsNotNull()
  // .IsEqual()
  // .IsMatch(regex)
  .AddError("Name is invalid");

Validation details

// Null if valid
var details = validationContext.When(person, p => p.Age)
  .IsNull()
  .AddError("Age must be set");

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

Check validation

// Override default ValidationSeverity
validationContext.ValidationSeverity = ValidationSeverity.Warning;

// Throw exception when details severity greater than ValidationContext.ValidationSeverity
validationContext.When(person, p => p.Address)
  .IsNull()
  .AddFatal("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.EnsureIsValid(person, p => p.Age);

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]))

High performance with non-expression constructions

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

validationContext.CreateScope("key");

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

Benchmarks (i7 9700k 3.60 GHz, 16Gb 3400 MHz)

  • Simpler expression = less costs
  • Try to use non-expression extensions in hot paths
  • Use CreateScope to not to repeat chained member calls (x => x.Property1.Property2[0].Property3)
  • Expression-based When extensions use expression compilation to get value (Invoke)
  • Composite indexers x => x.Collection[indexProvider.Parsed.Index] use expression compilation (DynamicInvoke)

Non-expression validation

Method Mean Error StdDev Max Iterations
Simple 1.421 us 0.0071 us 0.0653 us 1.581 us 925.0
CreateScope 1.287 us 0.0046 us 0.0431 us 1.394 us 971.0
IsValid 1.350 us 0.0042 us 0.0401 us 1.444 us 986.0
EnsureIsValid 1.374 us 0.0042 us 0.0401 us 1.475 us 987.0

Expression validation

Method Mean Error StdDev Max Iterations
SimpleExpression 52.181 us 0.2692 us 2.5770 us 60.106 us 998.0
ChainedExpression 59.643 us 0.3316 us 3.1521 us 68.800 us 984.0
ArrayAccessExpression 73.636 us 0.4902 us 4.6804 us 89.787 us 993.0
ChainedArrayAccessExpression 80.645 us 0.5602 us 5.3484 us 98.931 us 993.0
ChainedArrayAccess_DynamicInvoke_Expression 288.098 us 0.9826 us 9.3864 us 317.175 us 994.0
CreateScope_SimpleExpression 4.443 us 0.0156 us 0.1469 us 4.838 us 965.0
CreateScope_ChainedExpression 5.467 us 0.0301 us 0.2849 us 6.237 us 973.0
IsValid_Empty 4.642 us 0.0241 us 0.2275 us 5.275 us 970.0
IsValid_Expression 4.659 us 0.0192 us 0.1826 us 5.138 us 982.0
EnsureIsValid_Expression 4.664 us 0.0262 us 0.2496 us 5.450 us 991.0
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
3.1.10 1,024 10/12/2019
3.1.9 1,075 9/27/2019
3.1.8 995 9/25/2019
3.1.7 362 9/15/2019
3.1.6 323 9/8/2019
3.1.5 305 9/7/2019
3.1.4 305 9/7/2019
3.1.3 327 9/7/2019
3.1.2 310 8/25/2019
3.1.1 299 8/24/2019
3.1.0 300 8/23/2019
3.0.9 304 8/23/2019
3.0.8 316 8/22/2019
3.0.7 300 8/16/2019
3.0.6 334 7/30/2019
3.0.5 297 7/29/2019
3.0.4 321 7/29/2019
3.0.3 306 7/28/2019
3.0.2 303 7/21/2019