Apariencia
Inicio rápido: configurar, ejecutar y leer errores
Índice · Siguiente: uso cotidiano · Por qué validar
Este recorrido empieza con dos campos y amplía la misma composición hacia dominio y persistencia. No necesitas conocer árboles, definiciones internas ni razones tipadas para completar el primer paso.
1. Validar dos campos
csharp
using Greencore.platform.validators;
var plan = Validation.For<CustomerDraft>()
.FieldSequence(x => x.Email, rules => rules.Required().Email())
.FieldSequence(x => x.Name, rules => rules.Required().MinLength(2).MaxLength(80))
.Build();
var draft = new CustomerDraft("incorrecto", "");
var report = plan.Validate(draft);
foreach (var error in report.EnumerateDiagnostics(false))
Console.WriteLine($"{error.Path}: {error.Message}");
public sealed record CustomerDraft(string? Email, string? Name);El ejemplo devuelve un error de formato para Email y uno de presencia para Name. Dentro de cada campo, FieldSequence omite las reglas posteriores cuando falla una anterior. Los dos campos son independientes: un error en correo no impide revisar nombre.
Build() publica el plan reutilizable. Cada Validate recibe un candidato y crea su propio reporte. Los mensajes predeterminados están en español; los códigos son independientes del idioma del mensaje.
2. Personalizar únicamente lo necesario
csharp
.FieldSequence(x => x.Email, rules => rules
.Required("Escribe tu correo.")
.Email("Revisa el formato del correo.", code: "customer.email.invalid"))Este fragmento sustituye la configuración del campo del paso anterior. Cambiar solo el mensaje conserva el código predeterminado. No cambia la ejecución ni la ruta. Must y MustAsync requieren mensaje porque la biblioteca no conoce la intención de tu predicado.
3. Obtener errores de dominio sin redefinir reglas
csharp
using Greencore.Domain.Entity.Validators;
var validator = EntityValidator.For<CustomerDraft>().WithRules(plan);
var result = validator.Validate(draft);
foreach (var error in result.GetDiagnostics())
Console.WriteLine($"{error.Path}: {error.Message}");
foreach (var error in result.ForField(x => x.Email))
Console.WriteLine(error.Code);Estos fragmentos reutilizan las variables del primer paso; colócalos antes de la declaración del record en un archivo con instrucciones de nivel superior. En éxito ambas consultas devuelven una colección vacía. No hace falta acceder a result.Error para presentar mensajes. El namespace que contiene EntityValidator también contiene estas extensiones.
El error tipado continúa disponible en result.Error cuando necesitas inspección avanzada y el resultado ha fallado.
4. Añadir una consulta dependiente
csharp
var model = ModelValidator.For<CustomerDraft, QueryContext>(builder => builder
.Sequence(rules => rules
.Include(plan)
.MustAsync((value, context, token) =>
context.IsAvailableAsync(value.Email!, token),
"El correo ya está registrado.", "email.taken")));El archivo completo incluye el import de modelo y la implementación simulada de QueryContext. Una entrada inválida no consulta; una entrada válida ejecuta la comprobación. La secuencia protege aquí la consulta con todo el plan local. Si cada campo debe tener una consulta independiente, inclúyela en su propio FieldSequence.
MustAsync convierte el plan en asíncrono: usa ValidateAsync. El contexto y el token pertenecen a la ejecución; el plan conserva configuración.
5. Proteger cambios de estado
El mismo plan se registra como invariante local:
csharp
static Customer() => ConfigureInvariants(
Greencore.Domain.Entities.Invariants.For<CustomerDraft>()
.Include(CustomerRules.Local).Build());Este fragmento pertenece a la clase Customer del archivo completo, donde CustomerRules.Local es el plan de los dos campos. Rename construye un candidato con with y llama a TryChangeState. Un nombre vacío se rechaza y el estado anterior se conserva. El ejemplo de reserva explica por qué una invariante no equivale a una condición para autorizar operaciones.
6. Persistir y devolver rechazos
El ejemplo ejecutable entrega al puente ModelPersistence.ExecuteAsync la misma instantánea que desea escribir. La escritura se omite si el modelo falla. La persistencia mostrada es simulada; las restricciones y transacciones reales siguen siendo responsabilidad de la integración.
Para devolver un rechazo sencillo de operación:
csharp
using Greencore.Domain.Business.Errors;
using Greencore.Platform.Results;
var rejection = BusinessError.Create("customer.closed", "El cliente está cerrado.").ToResult();La fábrica da categoría, código y mensaje. ToResult() envuelve el mismo error sin crear una razón personalizada. Para un resultado con valor, usa ToResult<CustomerDraft>(); el tipo de retorno de un método no permite inferir ese argumento genérico en C#.
Ejecutar el recorrido completo
powershell
dotnet run --file examples/ValidationQuickStart.csLa salida muestra dos errores de entrada, la lectura directa desde dominio, el rechazo de un cambio de nombre, cero consultas para la entrada inválida y un guardado válido con una consulta.
El correo de este inicio rápido usa la comprobación técnica Email. Para que la aceptación coincida con la fábrica de un objeto de valor, continúa con valores. No hace falta sustituir el plan: cambia únicamente la regla del campo.