Receber por email
← Voltar para o catálogo

dotnet-fluent-validation

Generates FluentValidation validators for commands and queries. Includes common validation rules, custom validators, async validation, and integration with MediatR pipeline behaviors.

66estrelas
Atualizado há 5 meses

Ver no GitHub ↗

Como adicionar

/plugin marketplace add ronnythedev/dotnet-clean-architecture-skills

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/dotnet-fluent-validation/svg)](https://www.skillteca.com.br/skills/dotnet-fluent-validation)

Skills relacionadas

Ver todas de Desenvolvimento →

Alerta por categoria

Receba novas skills de Desenvolvimento toda segunda

1 email curto, só com as skills novas de Desenvolvimento. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

FluentValidation Rules Generator

Overview

FluentValidation provides a fluent interface for building strongly-typed validation rules:

  • Declarative rules - Readable, maintainable validation logic
  • Separation of concerns - Validation separate from domain
  • Integration with MediatR - Automatic validation via pipeline behavior
  • Custom validators - Reusable validation components

Quick Reference

| Validator Type | Purpose | Example | |----------------|---------|---------| | Built-in | Common validations | NotEmpty(), MaximumLength() | | Custom | Reusable rules | Must(BeValidEmail) | | Async | Database checks | MustAsync(BeUniqueEmail) | | Child | Nested objects | SetValidator(new AddressValidator()) | | Collection | List items | RuleForEach(x => x.Items) |

Validator Structure

/Application/{Feature}/
├── Create{Entity}/
│   ├── Create{Entity}Command.cs
│   └── Create{Entity}CommandValidator.cs    # Or inline in Command.cs
├── Update{Entity}/
│   └── Update{Entity}Command.cs             # Validator inline
└── Validators/
    ├── EmailValidator.cs                     # Reusable validators
    └── PhoneNumberValidator.cs

Template: Basic Command Validator

// src/{name}.application/{Feature}/Create{Entity}/Create{Entity}CommandValidator.cs
using FluentValidation;

namespace {name}.application.{feature}.Create{Entity};

public sealed class Create{Entity}CommandValidator : AbstractValidator<Create{Entity}Command>
{
    public Create{Entity}CommandValidator()
    {
        // ═══════════════════════════════════════════════════════════════
        // STRING VALIDATIONS
        // ═══════════════════════════════════════════════════════════════
        
        RuleFor(x => x.Name)
            .NotEmpty()
                .WithMessage("{Entity} name is required")
            .MaximumLength(100)
                .WithMessage("{Entity} name must not exceed 100 characters")
            .MinimumLength(2)
                .WithMessage("{Entity} name must be at least 2 characters");

        RuleFor(x => x.Description)
            .MaximumLength(500)
                .WithMessage("Description must not exceed 500 characters")
            .When(x => !string.IsNullOrEmpty(x.Description));

        // ═══════════════════════════════════════════════════════════════
        // GUID VALIDATIONS
        // ═══════════════════════════════════════════════════════════════
        
        RuleFor(x => x.OrganizationId)
            .NotEmpty()
                .WithMessage("Organization ID is required")
            .NotEqual(Guid.Empty)
                .WithMessage("Organization ID cannot be empty GUID");

        // ═══════════════════════════════════════════════════════════════
        // OPTIONAL FOREIGN KEY
        // ═══════════════════════════════════════════════════════════════
        
        RuleFor(x => x.ParentId)
            .NotEqual(Guid.Empty)
                .WithMessage("Parent ID cannot be empty GUID")
            .When(x => x.ParentId.HasValue);
    }
}

Template: Inline Validator (Preferred Pattern)

// src/{name}.application/{Feature}/Create{Entity}/Create{Entity}Command.cs
using FluentValidation;
using {name}.application.abstractions.messaging;
using {name}.domain.abstractions;

namespace {name}.application.{feature}.Create{Entity};

// ═══════════════════════════════════════════════════════════════
// COMMAND
// ═══════════════════════════════════════════════════════════════
public sealed record Create{Entity}Command(
    string Name,
    string? Description,
    Guid OrganizationId,
    string Email,
    decimal Amount,
    List<CreateItemRequest> Items) : ICommand<Guid>;

public sealed class CreateItemRequest
{
    public required string Name { get; init; }
    public int Quantity { get; init; }
}

// ═══════════════════════════════════════════════════════════════
// VALIDATOR (internal, same file)
// ═══════════════════════════════════════════════════════════════
internal sealed class Create{Entity}CommandValidator : AbstractValidator<Create{Entity}Command>
{
    public Create{Entity}CommandValidator()
    {
        RuleFor(x => x.Name)
            .NotEmpty()
            .MaximumLength(100);

        RuleFor(x => x.Email)
            .NotEmpty()
            .EmailAddress()
                .WithMessage("A valid email address is required");

        RuleFor(x => x.Amount)
            .GreaterThan(0)
                .WithMessage("Amount must be positive")
            .LessThanOrEqualTo(1_000_000)
                .WithMessage("Amount cannot exceed 1,000,000");

        RuleFor(x => x.Items)
            .NotEmpty()
                .WithMessage("At least one item is required")
            .Must(items => items.Count <= 100)
                .WithMessage("Cannot have more than 100 items");

        RuleForEach(x => x.Items)
            .ChildRules(item =>
            {
                item.RuleFor(i => i.Name)
                    .NotEmpty()
                    .MaximumLength(200);

                item.RuleFor(i => i.Quantity)
                    .GreaterThan(0)
                    .LessThanOrEqualTo(10000);
            });
    }
}

// ═══════════════════════════════════════════════════════════════
// HANDLER
// ═══════════════════════════════════════════════════════════════
internal sealed class Create{Entity}CommandHandler 
    : ICommandHandler<Create{Entity}Command, Guid>
{
    // ... implementation
}

Template: Async Validator with Database Check

// src/{name}.application/{Feature}/Create{Entity}/Create{Entity}CommandValidator.cs
using FluentValidation;
using {name}.domain.{aggregate};

namespace {name}.application.{feature}.Create{Entity};

internal sealed class Create{Entity}CommandValidator : AbstractValidator<Create{Entity}Command>
{
    private readonly I{Entity}Repository _{entity}Repository;
    private readonly IOrganizationRepository _organizationRepository;

    public Create{Entity}CommandValidator(
        I{Entity}Repository {entity}Repository,
        IOrganizationRepository organizationRepository)
    {
        _{entity}Repository = {entity}Repository;
        _organizationRepository = organizationRepository;

        // Sync validations first (fast)
        RuleFor(x => x.Name)
            .NotEmpty()
            .MaximumLength(100);

        RuleFor(x => x.OrganizationId)
            .NotEmpty();

        // Async validations (database calls)
        RuleFor(x => x.Name)
            .MustAsync(BeUniqueName)
                .WithMessage("A {entity} with this name already exists");

        RuleFor(x => x.OrganizationId)
            .MustAsync(OrganizationExists)
                .WithMessage("Organization does not exist");
    }

    private async Task<bool> BeUniqueName(string name, CancellationToken ct)
    {
        var existing = await _{entity}Repository.GetByNameAsync(name, ct);
        return existing is null;
    }

    private async Task<bool> OrganizationExists(Guid organizationId, CancellationToken ct)
    {
        return await _organizationRepository.ExistsAsync(organizationId, ct);
    }
}

Note: Prefer doing existence checks in the Handler rather than Validator for better separation of concerns and testability. Use async validation sparingly.

Template: Reusable Custom Validator

// src/{name}.application/Validators/EmailValidator.cs
using FluentValidation;
using FluentValidation.Validators;

namespace {name}.application.validators;

/// <summary>
/// Validates email format with stricter rules than built-in EmailAddress
/// </summary>
public sealed class StrictEmailValidator<T> : PropertyValidator<T, string>
{
    public override string Name => "StrictEmailValidator";

    public override bool IsValid(ValidationContext<T> context, string value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return true; // Let NotEmpty handle null/empty
        }

        // 
Compartilhar X LinkedIn