Dosaic.Plugins.Validations.AttributeValidation
1.2.9
dotnet add package Dosaic.Plugins.Validations.AttributeValidation --version 1.2.9
NuGet\Install-Package Dosaic.Plugins.Validations.AttributeValidation -Version 1.2.9
<PackageReference Include="Dosaic.Plugins.Validations.AttributeValidation" Version="1.2.9" />
<PackageVersion Include="Dosaic.Plugins.Validations.AttributeValidation" Version="1.2.9" />
<PackageReference Include="Dosaic.Plugins.Validations.AttributeValidation" />
paket add Dosaic.Plugins.Validations.AttributeValidation --version 1.2.9
#r "nuget: Dosaic.Plugins.Validations.AttributeValidation, 1.2.9"
#:package Dosaic.Plugins.Validations.AttributeValidation@1.2.9
#addin nuget:?package=Dosaic.Plugins.Validations.AttributeValidation&version=1.2.9
#tool nuget:?package=Dosaic.Plugins.Validations.AttributeValidation&version=1.2.9
Dosaic.Plugins.Validations.AttributeValidation
Dosaic.Plugins.Validations.AttributeValidation is a Dosaic plugin that enables attribute-based validation of .NET objects. It registers an IValidator implementation that recursively inspects models, validates properties against decorating attributes, and returns structured error results — all without requiring any manual wiring.
Installation
dotnet add package Dosaic.Plugins.Validations.AttributeValidation
Or as a package reference:
<PackageReference Include="Dosaic.Plugins.Validations.AttributeValidation" Version="" />
Configuration
No additional configuration is required. AttributeValidationPlugin implements IPluginServiceConfiguration and is discovered automatically by the Dosaic source generator at startup. It registers IValidator as a singleton:
// Registered automatically — no manual setup needed
services.AddSingleton<IValidator, AttributeValidator>();
Usage
Validating a model
Inject IValidator wherever you need validation:
using Dosaic.Plugins.Validations.Abstractions;
using Dosaic.Plugins.Validations.AttributeValidation.Validators;
public class OrderService(IValidator validator)
{
public async Task CreateOrder(OrderRequest request, CancellationToken ct)
{
var result = await validator.ValidateAsync(request, ct);
if (!result.IsValid)
{
foreach (var error in result.Errors)
Console.WriteLine($"[{error.Path}] {error.Code}: {error.Message}");
return;
}
// proceed...
}
}
Annotating models
Decorate properties (or classes) with validation attributes from the Validations static class:
using Dosaic.Plugins.Validations.AttributeValidation.Validators;
public class CreateUserRequest
{
[Validations.Required]
public string Id { get; set; }
[Validations.Required]
[Validations.String.MinLength(2)]
[Validations.String.MaxLength(50)]
public string Firstname { get; set; }
[Validations.Required]
[Validations.String.Regex(@"^[a-zA-Z]+$")]
public string Lastname { get; set; }
[Validations.String.Email]
public string Email { get; set; }
[Validations.String.Url]
public string ProfileUrl { get; set; }
[Validations.Int.Range(18, 120)]
public int Age { get; set; }
[Validations.Date.After(1900)]
[Validations.Date.Before(2010)]
public DateTime Birthdate { get; set; }
[Validations.Date.MinAge(18)]
[Validations.Date.MaxAge(100)]
public DateTime BirthdateByAge { get; set; }
[Validations.Bool.True]
public bool AcceptsTerms { get; set; }
[Validations.Required]
[Validations.Array.MinLength(1)]
[Validations.Array.MaxLength(5)]
public IList<AddressDto> Addresses { get; set; }
}
public class AddressDto
{
[Validations.Required]
[Validations.String.MinLength(2)]
public string City { get; set; }
}
Checking results
var request = new CreateUserRequest
{
Id = "u-001",
Firstname = "Jane",
Lastname = "Doe",
Email = "jane@example.com",
Age = 30,
Birthdate = new DateTime(1994, 6, 15),
AcceptsTerms = true,
Addresses = new List<AddressDto> { new() { City = "Berlin" } }
};
var result = await validator.ValidateAsync(request);
Console.WriteLine(result.IsValid); // true
var invalid = new CreateUserRequest { Id = "", Age = 10, AcceptsTerms = false };
var bad = await validator.ValidateAsync(invalid);
Console.WriteLine(bad.IsValid); // false
foreach (var e in bad.Errors)
Console.WriteLine($"{e.Path} — {e.Code}: {e.Message}");
// Id — Required: Field is required
// Age — Number.Range: Number must be at least 18 and at most 120
// AcceptsTerms — Bool.True: Value must be true
Validating a value with an explicit validator list
You can validate a bare value against an ad-hoc list of IValueValidator instances:
var validators = new IValueValidator[]
{
new Validations.RequiredAttribute(),
new Validations.String.MinLengthAttribute(5),
};
var result = await validator.ValidateAsync("Hi", validators);
// result.IsValid == false (too short)
Expression validation
The Validations.Expression attribute evaluates a DynamicExpresso expression. The expression receives this as an ExpressionContext with a Value property that holds the current field/property value. The interpreter is case-insensitive and supports late binding.
public class InvoiceDto
{
// Must be an even number
[Validations.Expression("Value % 2 == 0")]
public int InvoiceNumber { get; set; }
// String must start with "INV-"
[Validations.Expression("Value.StartsWith('INV-')")]
public string Reference { get; set; }
}
Expressions that throw an exception are treated as failing validation.
Nested and list validation
The validator recursively walks nested objects and collections. Error paths use / as a separator:
// Path examples:
// "Addresses" — error on the list property itself (e.g. Array.MinLength)
// "Addresses/0/City" — error on City of the first AddressDto
// "Matrix/0/0/Value" — nested lists follow the same pattern
Custom validators
Extend ValidationAttribute (or AsyncValidationAttribute for async logic) to create reusable validation attributes:
using Dosaic.Plugins.Validations.Abstractions;
using Dosaic.Plugins.Validations.AttributeValidation;
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class MustBePositiveEvenAttribute : ValidationAttribute
{
public override string ErrorMessage => "Value must be a positive even number";
public override string Code => "Custom.PositiveEven";
public override object Arguments => new { };
protected override bool IsValid(ValidationContext context)
=> context.Value is int n && n > 0 && n % 2 == 0;
}
// Usage:
public class MyModel
{
[MustBePositiveEven]
public int Count { get; set; }
}
For async validators override IsValidAsync directly:
public class UniqueEmailAttribute : AsyncValidationAttribute
{
public override string ErrorMessage => "Email is already taken";
public override string Code => "Custom.UniqueEmail";
public override object Arguments => new { };
public override async Task<bool> IsValidAsync(ValidationContext context, CancellationToken ct = default)
{
if (context.Value is not string email) return true;
var db = context.Services.GetRequiredService<IUserRepository>();
return !await db.ExistsAsync(email, ct);
}
}
Built-in Validators
Validations.Required
| Attribute | Arguments | Validation Code | Description |
|---|---|---|---|
[Validations.Required] |
— | Required |
Value must not be null; strings must not be empty or whitespace. TreatNullAsValid is false — a null value triggers failure. |
Validations.Expression
| Attribute | Arguments | Validation Code | Description |
|---|---|---|---|
[Validations.Expression(string expression)] |
expression |
Expression |
Evaluates a DynamicExpresso expression. Reference the property value via Value. Returns false if the expression throws. |
Validations.Array
Applicable to any IEnumerable property (excluding strings).
| Attribute | Arguments | Validation Code | Description |
|---|---|---|---|
[Validations.Array.Length(int minimum, int maximum)] |
minimum, maximum |
Array.Length |
Element count must be ≥ minimum and ≤ maximum. |
[Validations.Array.MinLength(int minimum)] |
minimum |
Array.MinLength |
Element count must be ≥ minimum. |
[Validations.Array.MaxLength(int maximum)] |
maximum |
Array.MaxLength |
Element count must be ≤ maximum. |
Validations.Bool
| Attribute | Arguments | Validation Code | Description |
|---|---|---|---|
[Validations.Bool.True] |
— | Bool.True |
Value must be true. |
[Validations.Bool.False] |
— | Bool.False |
Value must be false. |
Validations.Date
Age-based validators resolve the current UTC time from IDateTimeProvider (Chronos).
| Attribute | Arguments | Validation Code | Description |
|---|---|---|---|
[Validations.Date.Before(int year, int month = 1, int day = 1)] |
date |
Date.Before |
DateTime value must be on or before the specified date. |
[Validations.Date.After(int year, int month = 1, int day = 1)] |
date |
Date.After |
DateTime value must be on or after the specified date. |
[Validations.Date.Age(int minAge, int maxAge)] |
minAge, maxAge |
Date.Age |
Age derived from the DateTime must be between minAge and maxAge years. |
[Validations.Date.MinAge(int age)] |
age |
Date.MinAge |
Date must be at least age years ago. |
[Validations.Date.MaxAge(int age)] |
age |
Date.MaxAge |
Date must be at most age years ago. |
Validations.Int
Applies to int values.
| Attribute | Arguments | Validation Code | Description |
|---|---|---|---|
[Validations.Int.Range(int minimum, int maximum)] |
minimum, maximum |
Number.Range |
Value must be ≥ minimum and ≤ maximum. |
[Validations.Int.Min(int minimum)] |
minimum |
Number.Min |
Value must be ≥ minimum. |
[Validations.Int.Max(int maximum)] |
maximum |
Number.Max |
Value must be ≤ maximum. |
[Validations.Int.Positive] |
— | Number.Positive |
Value must be > 0. |
[Validations.Int.Negative] |
— | Number.Negative |
Value must be < 0. |
Validations.String
Applies to string values.
| Attribute | Arguments | Validation Code | Description |
|---|---|---|---|
[Validations.String.Length(int minimum, int maximum)] |
minimum, maximum |
String.Length |
String length must be ≥ minimum and ≤ maximum characters. |
[Validations.String.MinLength(int length)] |
length |
String.MinLength |
String must be at least length characters long. |
[Validations.String.MaxLength(int length)] |
length |
String.MaxLength |
String must be at most length characters long. |
[Validations.String.Email] |
— | String.Email |
String must be a valid email address (RFC-compliant, IDN-aware). |
[Validations.String.Url] |
— | String.Url |
String must be a valid absolute URI. |
[Validations.String.Regex(string pattern)] |
pattern |
String.Regex |
String must match the specified regular expression. |
Validation Error Structure
Each ValidationError in ValidationResult.Errors exposes the following properties:
| Property | Type | Description |
|---|---|---|
Path |
string |
/-separated path to the failing property (e.g. "Addresses/0/City"). Empty string for root-level validation. |
Code |
string |
Machine-readable validation code (see tables above). |
Message |
string |
Human-readable error message. |
Validator |
string |
Short name of the validator attribute (e.g."MinLength", "Required"). |
Arguments |
IDictionary<string, object> |
Attribute constructor arguments as a dictionary (e.g.{ "length": 5 }). |
ValidationResult.IsValid returns true when Errors is empty.
Null Handling
By default all built-in validators set TreatNullAsValid = true, meaning a null property value is skipped unless [Validations.Required] is also present. This lets you distinguish truly optional fields from required ones without cluttering annotations.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. 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. |
-
net10.0
- Dosaic.Hosting.Abstractions (>= 1.2.9)
- Dosaic.Plugins.Validations.Abstractions (>= 1.2.9)
- DynamicExpresso.Core (>= 2.19.3)
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.2.9 | 0 | 3/13/2026 |
| 1.2.8 | 81 | 3/9/2026 |
| 1.2.7 | 79 | 3/4/2026 |
| 1.2.6 | 106 | 2/19/2026 |
| 1.2.5 | 87 | 2/17/2026 |
| 1.2.4 | 110 | 2/13/2026 |
| 1.2.3 | 107 | 1/27/2026 |
| 1.2.2 | 298 | 12/16/2025 |
| 1.2.1 | 276 | 12/16/2025 |
| 1.2.0 | 434 | 12/11/2025 |
| 1.1.21 | 458 | 12/10/2025 |
| 1.1.20 | 422 | 11/18/2025 |
| 1.1.19 | 316 | 11/11/2025 |
| 1.1.18 | 211 | 10/14/2025 |
| 1.1.17 | 205 | 10/1/2025 |
| 1.1.16 | 214 | 9/25/2025 |
| 1.1.15 | 191 | 9/24/2025 |
| 1.1.14 | 193 | 9/24/2025 |
| 1.1.13 | 201 | 9/24/2025 |
| 1.1.12 | 340 | 9/16/2025 |