Apariencia
Caso de uso: validación contextual
Anterior: validación local · Casos de uso · Siguiente: formulario
Usa un plan contextual cuando una regla necesita datos o servicios que cambian por ejecución. El contexto puede contener repositorios, identidad excluida, permisos o información de la operación.
Flujo
text
Email
↓ Required
↓ Email
↓ MustAsync(email, contexto)
↓ consulta repositorio
↓ disponible / duplicado / fallo técnico
↓
ValidationReport o excepción técnicaLa consulta solo se ejecuta si presencia y formato se cumplen. Una excepción del repositorio se propaga desde el plan; no se convierte en un diagnóstico de duplicidad.
Ejemplo completo
csharp
using Greencore.platform.validators;
var plan = Validation.For<CustomerDraft, CustomerValidationContext>()
.Field(x => x.Email, field => field.Sequence(sequence => sequence
.Required("El correo es obligatorio.", "customer.email.required")
.Email("El correo no tiene un formato válido.", "customer.email.invalid")
.MustAsync(
async (email, context, token) =>
!await context.Repository.EmailExistsAsync(
email!,
context.ExcludedCustomerId,
token),
"El correo ya está registrado.",
"customer.email.duplicate")))
.Build();
var repository = new CustomerRepository(["[email protected]"]);
var candidate = new CustomerDraft(null, "[email protected]");
var context = new CustomerValidationContext(repository, candidate.Id);
var report = await plan.ValidateAsync(candidate, context);
foreach (var error in report.ForField(
(CustomerDraft x) => x.Email,
DiagnosticView.Summary))
{
Console.WriteLine($"{error.Code}: {error.Message}");
}
public sealed record CustomerDraft(Guid? Id, string? Email);
public sealed record CustomerValidationContext(ICustomerRepository Repository, Guid? ExcludedCustomerId);
public interface ICustomerRepository
{
ValueTask<bool> EmailExistsAsync(string email, Guid? excludedCustomerId, CancellationToken cancellationToken);
}
public sealed class CustomerRepository(IEnumerable<string> emails) : ICustomerRepository
{
private readonly HashSet<string> _emails = new(emails, StringComparer.OrdinalIgnoreCase);
public ValueTask<bool> EmailExistsAsync(string email, Guid? excludedCustomerId, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
return ValueTask.FromResult(_emails.Contains(email));
}
}Resultados posibles
| Correo | Consulta | Resultado |
|---|---|---|
null | No se ejecuta | customer.email.required |
incorrecto | No se ejecuta | customer.email.invalid |
[email protected] | Se ejecuta | customer.email.duplicate |
[email protected] | Se ejecuta | Reporte válido |
| Repositorio no disponible | Se ejecuta y lanza | Fallo técnico para el llamador |
La identidad excluida debe viajar en el contexto de esa ejecución. Evita capturarla en una definición compartida, porque distintos formularios pueden validar identidades diferentes al mismo tiempo.