Nedo.AspNet.Common.Validation.Dsl 1.9.1

dotnet add package Nedo.AspNet.Common.Validation.Dsl --version 1.9.1
                    
NuGet\Install-Package Nedo.AspNet.Common.Validation.Dsl -Version 1.9.1
                    
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="Nedo.AspNet.Common.Validation.Dsl" Version="1.9.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Nedo.AspNet.Common.Validation.Dsl" Version="1.9.1" />
                    
Directory.Packages.props
<PackageReference Include="Nedo.AspNet.Common.Validation.Dsl" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Nedo.AspNet.Common.Validation.Dsl --version 1.9.1
                    
#r "nuget: Nedo.AspNet.Common.Validation.Dsl, 1.9.1"
                    
#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.
#:package Nedo.AspNet.Common.Validation.Dsl@1.9.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Nedo.AspNet.Common.Validation.Dsl&version=1.9.1
                    
Install as a Cake Addin
#tool nuget:?package=Nedo.AspNet.Common.Validation.Dsl&version=1.9.1
                    
Install as a Cake Tool

Nedo.AspNet.Common.Validation.Dsl

A text-based DSL for defining validation rules at runtime — no recompilation required. Built on top of Nedo.AspNet.Common.Validation.Core, the DSL compiles rule text into IValidator<T> instances using System.Linq.Expressions for near-native performance.

Install

dotnet add package Nedo.AspNet.Common.Validation.Dsl

Quick Start

using Nedo.AspNet.Common.Validation.Dsl.Compilation;

var dsl = @"
rule UsernameRequired code ""VAL-001"" {
    when true
    then require Username else ""Username is required""
}

rule EmailFormat code ""VAL-002"" {
    when Email is not null
    then assert Email matches ""^[^@\s]+@[^@\s]+\.[^@\s]+$"" else ""Invalid email format""
}

rule AdultCheck code ""VAL-003"" {
    when Region == ""US""
    then assert Age >= 21 else ""Must be at least 21 in the US""
}";

// Compile DSL into IValidator<T>
var validator = RuleCompiler.CreateValidator<UserDto>(dsl);

// Validate
var result = validator.Validate(user);

if (!result.IsValid)
    foreach (var e in result.Errors)
        Console.WriteLine($"[{e.Code}] {e.Message}");

Architecture

DSL Text
   │
   ▼
Tokenizer ──▶ Token Stream
   │
   ▼
Parser (recursive-descent) ──▶ AST (Abstract Syntax Tree)
   │
   ▼
RuleCompiler ──▶ System.Linq.Expressions
   │
   ▼
IValidator<T>  (compiled, reusable)
  • Zero external dependencies — The tokenizer and parser are hand-written (no ANTLR or third-party grammar tools)
  • Expression-compiled — Rules are compiled into Expression<Func<...>> trees, not interpreted at runtime
  • IValidator<T> output — The compiled validator implements the same interface as Core and Fluent validators

DSL Grammar Reference

Rule Structure

rule <Name> code "<ErrorCode>" {
    when <Condition>
    then <Action> else "<ErrorMessage>"
}
Part Description
Name Identifier for the rule (used in debugging/logging)
code Machine-readable error code attached to ValidationError.Code
when Boolean condition — if true, the then action is evaluated
then Validation check to perform
else Error message (literal string or resource key for localization)

Conditions

Comparison Operators
Age >= 18
Status == "Active"
Score != 0
Price < 1000
Quantity <= 100
Rating > 3.5
Operator Description
== Equal
!= Not equal
> Greater than
>= Greater than or equal
< Less than
<= Less than or equal
Logical Operators
Age >= 18 and Status == "Active"
Role == "Admin" or Role == "Manager"
not (IsDeleted)
Operator Description
and Both conditions must be true
or At least one condition must be true
not Inverts the condition
Null Checks
Email is null
MiddleName is not null
Regex Matching
Email matches "^[^@\s]+@[^@\s]+\.[^@\s]+$"
Phone matches "^\+?[0-9]{10,15}$"
Boolean Fields
IsActive
not IsDeleted
true
false
Arithmetic Expressions
Price * Quantity > 1000
TotalAmount - Discount >= 0
Score / MaxScore > 0.5
Operator Description
+ Addition
- Subtraction
* Multiplication
/ Division
Nested Object Access
Address.City is not null
Address.ZipCode matches "^[0-9]{5}$"
Null-Safe Navigation
Address?.City is not null
Contact?.Phone?.Number is not null

Actions

require — Field Must Not Be Empty
rule NameRequired code "VAL-001" {
    when true
    then require Name else "Name is required"
}

Fails if the field is null, an empty string, or a whitespace-only string.

assert — Condition Must Be True
rule MinAge code "VAL-002" {
    when Country == "US"
    then assert Age >= 21 else "Must be 21+ in the US"
}

Fails if the condition evaluates to false.

forbid — Unconditional Failure
rule BlockSuspended code "VAL-003" {
    when Status == "Suspended"
    then forbid else "Suspended accounts cannot perform this action"
}

Always fails when the when condition is met. Useful for "deny" rules.

cardinality — Collection Count Checks
rule MaxItems code "VAL-004" {
    when true
    then cardinality OrderItems <= 50 else "Maximum 50 items per order"
}

rule HasActiveItems code "VAL-005" {
    when true
    then cardinality Items[IsActive] > 0 else "At least one active item required"
}
Syntax Description
cardinality List == N Exact count
cardinality List >= N At least N items
cardinality List <= N At most N items
cardinality List[Filter] > 0 Filtered count (where Filter is a boolean field)

Advanced Examples

Cross-Field Validation

rule PasswordMatch code "VAL-010" {
    when Password is not null
    then assert Password == ConfirmPassword else "Passwords do not match"
}

rule DateRange code "VAL-011" {
    when StartDate is not null and EndDate is not null
    then assert EndDate >= StartDate else "End date must be after start date"
}

Conditional Required Fields

rule CompanyRequired code "VAL-020" {
    when AccountType == "Business"
    then require CompanyName else "Company name is required for business accounts"
}

rule TaxIdRequired code "VAL-021" {
    when AccountType == "Business" and Country == "US"
    then require TaxId else "Tax ID is required for US business accounts"
}

Nested Object Validation

rule AddressRequired code "VAL-030" {
    when ShippingMethod != "Digital"
    then require Address.Street else "Street address is required for physical delivery"
}

rule ZipCodeFormat code "VAL-031" {
    when Address.Country == "US"
    then assert Address.ZipCode matches "^[0-9]{5}(-[0-9]{4})?$"
    else "Invalid US zip code format"
}

Arithmetic Validation

rule MaxOrderValue code "VAL-040" {
    when true
    then assert UnitPrice * Quantity <= 100000
    else "Order value cannot exceed 100,000"
}

rule DiscountLimit code "VAL-041" {
    when DiscountPercent is not null
    then assert DiscountPercent >= 0 and DiscountPercent <= 50
    else "Discount must be between 0% and 50%"
}

Localization

Pass a ResourceManager to resolve else strings as resource keys:

using System.Resources;

var validator = RuleCompiler.CreateValidator<UserDto>(
    dsl,
    MyResources.ResourceManager);

// If MyResources has a key "Username is required" with translations,
// the localized string is returned based on culture
var resultId = validator.Validate(user, "id");  // Indonesian
var resultEn = validator.Validate(user, "en");  // English

How It Works

  1. The else string (e.g. "Username is required") is used as a resource key lookup
  2. If a translation exists for the current culture, the localized message is returned
  3. If no translation found, the literal else string is used as-is
  4. This allows gradual localization — start with hardcoded strings, add translations later

Composing with Other Validators

The DSL-compiled validator implements IValidator<T>, so it works with CompositeValidator<T>:

using Nedo.AspNet.Common.Validation.Core;

var dslValidator = RuleCompiler.CreateValidator<UserDto>(dsl);
var fluentValidator = new UserValidator(); // AbstractValidator<UserDto>

var composite = new CompositeValidator<UserDto>()
    .Add(fluentValidator)    // Compile-time rules
    .Add(dslValidator)       // Runtime-defined rules
    .Add((dto, culture) =>   // Inline rule
        RequiredValidator.Validate(dto.Id, "Id", culture ?? "en"));

var result = composite.Validate(user, "id");

Loading Rules from Files or Database

Since DSL rules are plain text, they can be loaded from any source:

// From a file
var dsl = File.ReadAllText("validation-rules.dsl");
var validator = RuleCompiler.CreateValidator<OrderDto>(dsl);

// From database
var rules = await db.ValidationRules
    .Where(r => r.EntityType == "Order" && r.IsActive)
    .Select(r => r.RuleText)
    .ToListAsync();

var combinedDsl = string.Join("\n", rules);
var validator = RuleCompiler.CreateValidator<OrderDto>(combinedDsl);

This enables:

  • Admin-configurable rules without code deployment
  • Per-tenant validation in multi-tenant applications
  • A/B testing different validation rule sets

  • Nedo.AspNet.Common.Validation.Core — Core engine, 120+ static validators, CompositeValidator<T>
  • Nedo.AspNet.Common.Validation.Fluent — Fluent AbstractValidator<T> API with chained rules
Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows 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
1.9.1 81 2/13/2026
1.9.0 77 2/12/2026
1.8.0 87 2/12/2026
1.7.2 79 2/11/2026
1.7.1 79 2/11/2026
1.7.0 88 1/25/2026
1.6.0 83 1/18/2026