Skip to content

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écnica

La 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 ​

CorreoConsultaResultado
nullNo se ejecutacustomer.email.required
incorrectoNo se ejecutacustomer.email.invalid
[email protected]Se ejecutacustomer.email.duplicate
[email protected]Se ejecutaReporte válido
Repositorio no disponibleSe ejecuta y lanzaFallo 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.

Uso personal limitado. Uso comercial sujeto a autorización escrita.