Pitasoft.Validation
5.1.3
dotnet add package Pitasoft.Validation --version 5.1.3
NuGet\Install-Package Pitasoft.Validation -Version 5.1.3
<PackageReference Include="Pitasoft.Validation" Version="5.1.3" />
<PackageVersion Include="Pitasoft.Validation" Version="5.1.3" />
<PackageReference Include="Pitasoft.Validation" />
paket add Pitasoft.Validation --version 5.1.3
#r "nuget: Pitasoft.Validation, 5.1.3"
#:package Pitasoft.Validation@5.1.3
#addin nuget:?package=Pitasoft.Validation&version=5.1.3
#tool nuget:?package=Pitasoft.Validation&version=5.1.3
Pitasoft.Validation
A flexible and high-performance C# validation library designed to validate objects using multiple strategies, including Data Annotations, custom rules, and a fluent API.
English
Features
- Multiple Validation Strategies:
- Data Annotations: Leverage existing
System.ComponentModel.DataAnnotationsattributes. - Fluent API: Define validation rules using a clean, readable syntax with 25+ built-in methods.
- Custom Rules: Implement complex validation logic via delegates or custom
ICheckerimplementations.
- Data Annotations: Leverage existing
- Hierarchical Validation: Support for child validators to validate nested object graphs.
- Collection Validation: Validate each element of a collection with indexed error paths (
Items[0].Name). - Conditional Validation: Apply rules only when a condition is met (
When/Unless). - Asynchronous Validation: Support for async rules via
MustAsyncandValidateObjectAsync. - Stop on First Failure: Optionally stop evaluating rules for a property after the first failure.
- Reusable Validators: Define validators as reusable classes using
AbstractValidator<T>. - Error Codes: Attach optional error codes to rules for programmatic identification.
- High Performance: Optimized using expression compilation and caching for property access and display names.
- Pitasoft.Error Integration: Returns errors in the standard
ErrorCollectionformat from thePitasoft.Errorlibrary. - Strongly Typed: Full support for generics and lambda expressions to avoid magic strings.
Installation
Add the Pitasoft.Validation package to your project:
dotnet add package Pitasoft.Validation
Note: Depends on Pitasoft.Error.
Getting Started
1. Data Annotations Validation
The simplest way to start is using existing attributes on your models.
public class User
{
[Required]
[StringLength(50)]
public string Name { get; set; }
[Range(18, 99)]
public int Age { get; set; }
}
// Usage with Extension Methods (easiest)
var user = new User { Name = "", Age = 10 };
ErrorCollection? errors = user.ValidateWithAttributes();
// Or using a manual validator
var validator = new Validator<User>(new ValidationAttributeChecker<User>());
errors = validator.ValidateObject(user);
if (errors?.Any() == true)
{
// Handle errors
}
else
{
// Valid
}
2. Fluent API Rules
Define rules programmatically without modifying your model classes.
// Usage with Extension Methods
var errors = user.ValidateWithValidator(v =>
{
v.For(u => u.Name)
.NotNullOrEmpty("Name is required")
.MinLength(2, "Name is too short")
.MaxLength(50, "Name is too long")
.Matches(@"^[a-zA-Z\s]+$", "Name must contain only letters");
v.For(u => u.Age)
.Range(18, 99, "Age must be between 18 and 99");
v.For(u => u.Email)
.Email("Invalid email address");
});
// Or using a manual validator
var validator = new Validator<User>();
validator.For(u => u.Name).NotNullOrEmpty("Required");
errors = validator.ValidateObject(user);
3. Nested Objects (Child Validators)
Validate complex object graphs by registering validators for child properties.
public class Order
{
public Address ShippingAddress { get; set; }
}
var addressValidator = new Validator<Address>(new ValidationAttributeChecker<Address>());
var orderValidator = new Validator<Order>();
// Registering child validator
orderValidator.RegisterChildValidator(o => o.ShippingAddress, addressValidator);
var errors = orderValidator.ValidateObject(order);
// Errors for address will have keys like "ShippingAddress.City"
4. Integration with Pitasoft.Error
All validation results are returned as an ErrorCollection, allowing for easy aggregation and consistent error handling across your application.
public void ProcessUser(User user)
{
var validator = CreateUserValidator();
if (!validator.TryValidate(user, out var errors))
{
// Handle errors (e.g., return HTTP 400 with the collection)
throw new ValidationException(errors);
}
}
Fluent API Reference
String Methods
| Method | Description |
|---|---|
NotNullOrEmpty(msg) |
Validates that the value is not null or empty |
MinLength(min, msg) |
Minimum string length |
MaxLength(max, msg) |
Maximum string length |
Length(exact, msg) |
Exact string length |
LengthBetween(min, max, msg) |
String length within a range |
Matches(pattern, msg) |
Value matches a regular expression |
Email(msg) |
Valid email address format |
Null and Equality Methods
| Method | Description |
|---|---|
NotNull(msg) |
Validates that the value is not null |
Null(msg) |
Validates that the value is null |
Equal(expected, msg) |
Value equals the expected value |
NotEqual(unexpected, msg) |
Value does not equal the given value |
Numeric / Comparable Methods
| Method | Description |
|---|---|
Range(min, max, msg) |
Value is within the specified range (inclusive) |
GreaterThan(min, msg) |
Value is strictly greater than the minimum |
GreaterThanOrEqual(min, msg) |
Value is greater than or equal to the minimum |
LessThan(max, msg) |
Value is strictly less than the maximum |
LessThanOrEqual(max, msg) |
Value is less than or equal to the maximum |
Collection Methods
| Method | Description |
|---|---|
NotEmpty(msg) |
Collection is not empty |
MinItems(min, msg) |
Collection has at least the specified number of elements |
MaxItems(max, msg) |
Collection has at most the specified number of elements |
Custom and Conditional Methods
| Method | Description |
|---|---|
Must(predicate, msg) |
Custom rule with access to the full object instance |
MustAsync(predicate, msg) |
Async custom rule |
When(condition, configure) |
Apply rules only when the condition is true |
Unless(condition, configure) |
Apply rules only when the condition is false |
StopOnFirstFailure() |
Stop evaluating rules for this property after the first failure |
All methods accept an optional code parameter to attach an error code for programmatic identification:
validator.For(u => u.Name).NotNullOrEmpty("Name is required", code: "NAME_REQUIRED");
Error Message Placeholders
| Placeholder | Value |
|---|---|
{0} |
Visible property name (from DisplayAttribute if present) |
{1} |
Current property value |
Advanced Concepts
Reusable Validators with AbstractValidator<T>
Define validators as reusable classes by inheriting from AbstractValidator<T>:
public class UserValidator : AbstractValidator<User>
{
protected override void Configure()
{
For(u => u.Name)
.NotNullOrEmpty("Name is required")
.MaxLength(50, "Name is too long");
For(u => u.Age)
.Range(18, 99, "Age must be between 18 and 99");
}
}
// Usage
var validator = new UserValidator();
var errors = validator.ValidateObject(user);
Conditional Validation (When / Unless)
Apply rules only when a condition is met:
validator.For(u => u.CompanyName)
.When(u => u.IsCompany, v => v.NotNullOrEmpty("Company name is required for companies"));
validator.For(u => u.PersonalId)
.Unless(u => u.IsCompany, v => v.NotNullOrEmpty("Personal ID is required for individuals"));
Collection Validation (ForEach)
Validate each element of a collection individually with indexed error paths:
public class Order
{
public List<OrderLine> Lines { get; set; }
}
var validator = new Validator<Order>();
validator.ForEach(o => o.Lines, line =>
{
line.For(l => l.Quantity).GreaterThan(0, "Quantity must be greater than zero");
line.For(l => l.ProductId).NotNullOrEmpty("Product is required");
});
var errors = validator.ValidateObject(order);
// Errors will have keys like "Lines[0].Quantity", "Lines[1].ProductId"
Asynchronous Validation
Use MustAsync for rules that require async operations (e.g., database lookups):
validator.For(u => u.Email)
.MustAsync(async u => !await emailService.ExistsAsync(u.Email), "Email already in use");
var errors = await validator.ValidateObjectAsync(user);
Cross-property Validation
The Must method receives the full object instance, enabling validation that depends on multiple properties:
validator.For(u => u.YearsOfExperience)
.Must(u => u.YearsOfExperience >= u.MinRequiredExperience,
"Years of experience for {0} must be at least the minimum required");
Stop on First Failure
Stop evaluating rules for a property after the first failure:
validator.For(u => u.Name)
.StopOnFirstFailure()
.NotNullOrEmpty("Name is required")
.MaxLength(50, "Name is too long");
IChecker
Implement custom checkers to extend the validation engine. An IChecker can return structured errors with relative paths. This is useful for complex types or when you need to integrate with external validation libraries.
Performance
Validator<T> is designed for high performance:
- Compiled expressions for fast property access.
- Cached display name lookups (from
DisplayAttribute). - Efficient rule grouping and property-based validation.
Español
Características
- Múltiples estrategias de validación:
- Data Annotations: Aprovecha los atributos de
System.ComponentModel.DataAnnotationsexistentes. - API fluida: Define reglas con una sintaxis clara y legible con más de 25 métodos integrados.
- Reglas personalizadas: Implementa lógica compleja mediante delegados o implementaciones propias de
IChecker.
- Data Annotations: Aprovecha los atributos de
- Validación jerárquica: Soporta validadores hijos para validar grafos de objetos anidados.
- Validación de colecciones: Valida cada elemento de una colección con rutas de error indexadas (
Items[0].Name). - Validación condicional: Aplica reglas solo cuando se cumple una condición (
When/Unless). - Validación asíncrona: Soporte para reglas async mediante
MustAsyncyValidateObjectAsync. - Detener al primer fallo: Opcionalmente detiene la evaluación de reglas de una propiedad tras el primer fallo.
- Validadores reutilizables: Define validadores como clases reutilizables usando
AbstractValidator<T>. - Códigos de error: Adjunta códigos de error opcionales a las reglas para identificación programática.
- Alto rendimiento: Optimizado con compilación de expresiones y cachés para acceso a propiedades y nombres visibles.
- Integración con Pitasoft.Error: Devuelve errores en el formato estándar
ErrorCollectionde la libreríaPitasoft.Error. - Tipado fuerte: Soporta genéricos y expresiones lambda para evitar "magic strings".
Instalación
Agrega el paquete Pitasoft.Validation a tu proyecto:
dotnet add package Pitasoft.Validation
Nota: Depende de Pitasoft.Error.
Primeros pasos
1. Validación con Data Annotations
La forma más simple de empezar es usando los atributos ya presentes en tus modelos.
public class User
{
[Required]
[StringLength(50)]
public string Name { get; set; }
[Range(18, 99)]
public int Age { get; set; }
}
// Uso con Métodos de Extensión (lo más fácil)
var user = new User { Name = "", Age = 10 };
ErrorCollection? errors = user.ValidateWithAttributes();
// O usando un validador manual
var validator = new Validator<User>(new ValidationAttributeChecker<User>());
errors = validator.ValidateObject(user);
if (errors?.Any() == true)
{
// Manejar errores
}
else
{
// Válido
}
2. Reglas con API fluida
Define reglas programáticamente sin modificar tus clases de modelo.
// Uso con Métodos de Extensión
var errors = user.ValidateWithValidator(v =>
{
v.For(u => u.Name)
.NotNullOrEmpty("El nombre es obligatorio")
.MinLength(2, "El nombre es demasiado corto")
.MaxLength(50, "El nombre es demasiado largo")
.Matches(@"^[a-zA-ZáéíóúÁÉÍÓÚñÑ\s]+$", "El nombre solo puede contener letras");
v.For(u => u.Age)
.Range(18, 99, "La edad debe estar entre 18 y 99");
v.For(u => u.Email)
.Email("Dirección de email no válida");
});
// O usando un validador manual
var validator = new Validator<User>();
validator.For(u => u.Name).NotNullOrEmpty("Obligatorio");
errors = validator.ValidateObject(user);
3. Objetos anidados (validadores hijos)
Valida grafos complejos registrando validadores para propiedades hijas.
public class Order
{
public Address ShippingAddress { get; set; }
}
var addressValidator = new Validator<Address>(new ValidationAttributeChecker<Address>());
var orderValidator = new Validator<Order>();
// Registrando validador hijo
orderValidator.RegisterChildValidator(o => o.ShippingAddress, addressValidator);
var errors = orderValidator.ValidateObject(order);
// Los errores del address tendrán claves como "ShippingAddress.City"
4. Integración con Pitasoft.Error
Todos los resultados se devuelven como ErrorCollection, lo que permite una agregación sencilla y un manejo de errores consistente en tu aplicación.
public void ProcessUser(User user)
{
var validator = CreateUserValidator();
if (!validator.TryValidate(user, out var errors))
{
// Manejar errores (p.ej., devolver HTTP 400 con la colección)
throw new ValidationException(errors);
}
}
Referencia de la API fluida
Métodos para cadenas de texto
| Método | Descripción |
|---|---|
NotNullOrEmpty(msg) |
Valida que el valor no sea nulo ni vacío |
MinLength(min, msg) |
Longitud mínima de la cadena |
MaxLength(max, msg) |
Longitud máxima de la cadena |
Length(exact, msg) |
Longitud exacta de la cadena |
LengthBetween(min, max, msg) |
Longitud de la cadena dentro de un rango |
Matches(pattern, msg) |
El valor coincide con una expresión regular |
Email(msg) |
Formato de dirección de email válido |
Métodos de nulidad e igualdad
| Método | Descripción |
|---|---|
NotNull(msg) |
Valida que el valor no sea nulo |
Null(msg) |
Valida que el valor sea nulo |
Equal(expected, msg) |
El valor es igual al esperado |
NotEqual(unexpected, msg) |
El valor es distinto al indicado |
Métodos numéricos / comparables
| Método | Descripción |
|---|---|
Range(min, max, msg) |
El valor está dentro del rango especificado (inclusive) |
GreaterThan(min, msg) |
El valor es estrictamente mayor que el mínimo |
GreaterThanOrEqual(min, msg) |
El valor es mayor o igual que el mínimo |
LessThan(max, msg) |
El valor es estrictamente menor que el máximo |
LessThanOrEqual(max, msg) |
El valor es menor o igual que el máximo |
Métodos para colecciones
| Método | Descripción |
|---|---|
NotEmpty(msg) |
La colección no está vacía |
MinItems(min, msg) |
La colección tiene al menos el número de elementos indicado |
MaxItems(max, msg) |
La colección tiene como máximo el número de elementos indicado |
Métodos personalizados y condicionales
| Método | Descripción |
|---|---|
Must(predicate, msg) |
Regla personalizada con acceso a la instancia completa del objeto |
MustAsync(predicate, msg) |
Regla personalizada asíncrona |
When(condition, configure) |
Aplica reglas solo cuando la condición es verdadera |
Unless(condition, configure) |
Aplica reglas solo cuando la condición es falsa |
StopOnFirstFailure() |
Detiene la evaluación de reglas de esta propiedad tras el primer fallo |
Todos los métodos aceptan un parámetro opcional code para adjuntar un código de error para identificación programática:
validator.For(u => u.Name).NotNullOrEmpty("El nombre es obligatorio", code: "NOMBRE_REQUERIDO");
Marcadores de posición en mensajes de error
| Marcador | Valor |
|---|---|
{0} |
Nombre visible de la propiedad (desde DisplayAttribute si está presente) |
{1} |
Valor actual de la propiedad |
Conceptos avanzados
Validadores reutilizables con AbstractValidator<T>
Define validadores como clases reutilizables heredando de AbstractValidator<T>:
public class UserValidator : AbstractValidator<User>
{
protected override void Configure()
{
For(u => u.Name)
.NotNullOrEmpty("El nombre es obligatorio")
.MaxLength(50, "El nombre es demasiado largo");
For(u => u.Age)
.Range(18, 99, "La edad debe estar entre 18 y 99");
}
}
// Uso
var validator = new UserValidator();
var errors = validator.ValidateObject(user);
Validación condicional (When / Unless)
Aplica reglas solo cuando se cumple una condición:
validator.For(u => u.CompanyName)
.When(u => u.IsCompany, v => v.NotNullOrEmpty("El nombre de empresa es obligatorio para empresas"));
validator.For(u => u.PersonalId)
.Unless(u => u.IsCompany, v => v.NotNullOrEmpty("El DNI es obligatorio para personas físicas"));
Validación de colecciones (ForEach)
Valida cada elemento de una colección individualmente con rutas de error indexadas:
public class Order
{
public List<OrderLine> Lines { get; set; }
}
var validator = new Validator<Order>();
validator.ForEach(o => o.Lines, line =>
{
line.For(l => l.Quantity).GreaterThan(0, "La cantidad debe ser mayor que cero");
line.For(l => l.ProductId).NotNullOrEmpty("El producto es obligatorio");
});
var errors = validator.ValidateObject(order);
// Los errores tendrán claves como "Lines[0].Quantity", "Lines[1].ProductId"
Validación asíncrona
Usa MustAsync para reglas que requieren operaciones asíncronas (p.ej., consultas a base de datos):
validator.For(u => u.Email)
.MustAsync(async u => !await emailService.ExistsAsync(u.Email), "El email ya está en uso");
var errors = await validator.ValidateObjectAsync(user);
Validación cruzada entre propiedades
El método Must recibe la instancia completa del objeto, facilitando validaciones que dependan de múltiples campos:
validator.For(u => u.YearsOfExperience)
.Must(u => u.YearsOfExperience >= u.MinRequiredExperience,
"Los años de experiencia para {0} deben ser al menos el mínimo requerido");
Detener al primer fallo
Detiene la evaluación de reglas de una propiedad tras el primer fallo:
validator.For(u => u.Name)
.StopOnFirstFailure()
.NotNullOrEmpty("El nombre es obligatorio")
.MaxLength(50, "El nombre es demasiado largo");
IChecker
Implementa validadores propios (IChecker) para extender el motor de validación. Un IChecker puede devolver errores estructurados con rutas relativas, ideal para integraciones o tipos complejos.
Rendimiento
Validator<T> está diseñado para alto rendimiento:
- Expresiones compiladas para un acceso rápido a propiedades.
- Caché de nombres visibles (desde
DisplayAttribute). - Agrupación eficiente de reglas y validación basada en propiedades.
Autor
Sebastián Martínez Pérez
Licencia
Copyright © 2020-2026 Pitasoft, S.L. Distribuido bajo la licencia LICENSE.txt incluida en este repositorio.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. 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. 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 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
- Pitasoft.Error (>= 5.3.2)
-
net8.0
- Pitasoft.Error (>= 5.3.2)
-
net9.0
- Pitasoft.Error (>= 5.3.2)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Pitasoft.Validation:
| Package | Downloads |
|---|---|
|
Pitasoft.Validation.Rules
Implement rules to validate objects |
|
|
Pitasoft.FluentValidation
Provides integration with FluentValidation for Pitasoft.Validation |
GitHub repositories
This package is not used by any popular GitHub repositories.