Phema.Validation 3.0.7

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.0.7
dotnet add package Phema.Validation --version 3.0.7
<PackageReference Include="Phema.Validation" Version="3.0.7" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Phema.Validation --version 3.0.7
The NuGet Team does not provide support for this client. Please contact its maintainers for support.

Phema.Validation

Build Status
Nuget

C# strongly typed expression-based validation library for .NET built on extension methods

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 OR)
validationContext.When(person, p => p.Name)
  .IsNull()
  .HasLengthGreater(20)
  // .IsNotNull()
  // .IsEqual()
  // .IsMatch(regex)
  .AddError("Name is invalid");

Validation details

// Null if valid
var validationDetails = 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 =&gt; x.Property1.Property2[0].Property3)
  • Expression-based When extensions use expression compilation to get value (Invoke)
  • Composite indexers x =&gt; 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 |

Phema.Validation

Build Status
Nuget

C# strongly typed expression-based validation library for .NET built on extension methods

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 OR)
validationContext.When(person, p => p.Name)
  .IsNull()
  .HasLengthGreater(20)
  // .IsNotNull()
  // .IsEqual()
  // .IsMatch(regex)
  .AddError("Name is invalid");

Validation details

// Null if valid
var validationDetails = 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 =&gt; x.Property1.Property2[0].Property3)
  • Expression-based When extensions use expression compilation to get value (Invoke)
  • Composite indexers x =&gt; 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 |

GitHub repositories (0)

This package is not used by any popular GitHub repositories.

Version History

Version Downloads Last updated
3.1.10 122 10/12/2019
3.1.9 187 9/27/2019
3.1.8 157 9/25/2019
3.1.7 126 9/15/2019
3.1.6 86 9/8/2019
3.1.5 72 9/7/2019
3.1.4 72 9/7/2019
3.1.3 93 9/7/2019
3.1.2 73 8/25/2019
3.1.1 72 8/24/2019
3.1.0 72 8/23/2019
3.0.9 73 8/23/2019
3.0.8 74 8/22/2019
3.0.7 75 8/16/2019
3.0.6 68 7/30/2019
3.0.5 72 7/29/2019
3.0.4 75 7/29/2019
3.0.3 74 7/28/2019
3.0.2 72 7/21/2019
Show less