ASP.NET Core - Data Annotations


  Neste artigo vou apresentar os principais atributos Data Annotations que podem    simplificar o tratamento de dados na sua aplicação

Os atributos Data Annotations são usados para especificar metadados sobre as propriedades de uma classe, sendo que , esses atributos fornecem informações adicionais sobre como os dados devem ser validados, formatados ou exibidos.

Os atributos Data Annotations são usados principalmente para a validação de dados de entrada em um aplicativo ASP.NET Core e permitem definir regras de validação para as propriedades de um modelo, facilitando a verificação da validade dos dados antes que eles sejam processados pelo aplicativo.

Por que e quando usar Data Annotations(DAs)

As anotações de dados (DAs) são usadas para fornecer metadados ao seu código, o que pode ajudar a controlar o comportamento do seu código. Ao usar DAs em seu código, você pode garantir que seu aplicativo seja mais robusto, confiável e de fácil manutenção.

Aqui estão algumas razões pelas quais você pode querer usar Data Annotations:

  1. Validação de dados: Os DAs podem ser usados para impor regras de validação nos dados de entrada. Por exemplo, você pode usar o atributo [Required] para especificar que uma propriedade deve ter um valor não nulo ou não vazio. Isso ajuda a garantir a integridade dos dados antes de serem processados pelo aplicativo.
     
  2. Formatação de dados: Os DAs podem ser usados para controlar como os dados são formatados e exibidos. Por exemplo, o atributo [DisplayFormat] permite especificar o formato de exibição de um valor de data ou número. Isso é especialmente útil ao exibir dados em views ou ao serializar objetos para diferentes formatos, como JSON.
     
  3. Personalização da interface do usuário: Os DAs podem ser usados para fornecer informações para a geração automática de formulários e controles de edição na interface do usuário. Por exemplo, o atributo [DisplayName] permite especificar o rótulo a ser exibido para uma propriedade em um formulário gerado automaticamente.
     
  4. Documentação do código: Os DAs podem servir como documentação do código, fornecendo informações sobre as expectativas de uso e validação dos dados. Isso torna o código mais legível e facilita a manutenção.
     
  5. Integração com outros recursos: Os DAs podem ser usados em conjunto com outros recursos do ASP.NET Core, como Model Binders, para facilitar o processamento e a validação dos dados. Por exemplo, o atributo [Bind] pode ser usado para especificar quais propriedades de um modelo devem ser vinculadas durante a ligação de modelo.

Principais atributos DAs

Existem vários atributos Data Annotations disponíveis no ASP.NET Core que são amplamente usados para validar e formatar dados. Aqui estão alguns dos principais atributos Data Annotations:

1- Required

A anotação de dados Required é usada para marcar uma propriedade como obrigatória. Isso significa que a propriedade não pode ser nula ou vazia. Se a propriedade não estiver definida, será gerado um erro de validação.

public class Pessoa
{
   [Required]
  
public string? Nome { get; set; }

   [Required]
  
public string? Sobrenome { get; set; }

}

Parâmetros usados:

public class Pessoa
{
   [Required(ErrorMessage="O nome é obrigatório", AllowEmptyStrings=false)]
  
public string? Nome { get; set; }

   [Required]
  
public string? Sobrenome { get; set; }

}

2- Range

A anotação de dados Range é usada para definir um intervalo para propriedades numéricas. Você pode especificar os valores mínimo e máximo para a propriedade.

public class Produto
{
  [Range(1, 1000)]
 
public int Quantidade { get; set; }

  [Range(0.0,9999.99)]
 
public decimal Preco { get; set; }
}
 

3- StringLength

A anotação de dados StringLength é usada para definir o comprimento máximo de uma propriedade string. Podemos também definir o tamanho mínimo.

public class Cliente
{
   [StringLength(80, MinimumLength = 4)]
  
public string? Nome { get; set; }
}
 

4- RegularExpression

A anotação de dados RegularExpression é usada para validar uma propriedade em relação a um padrão de expressão regular. Ele é usado para aplicar um padrão de expressão regular a uma propriedade de string em uma classe de modelo ASP.NET Core.

Nesse caso, o padrão de expressão regular especifica que a string deve consistir apenas em caracteres alfanuméricos (letras e dígitos).

public class Conta
{
   [RegularExpression(
@"^[a-zA-Z0-9]*$")]
  
public string? NomeUsuario { get; set; }

   [RegularExpression(
".+\\@.+\\..+", ErrorMessage = "Informe um email válido...")]
  
public string? Email { get; set; }
}
 

5- DisplayFormat - Aplica um formato definido a uma propriedade

public class Pedido
{
   [DisplayFormat(DataFormatString =
"mm/dd/yyyy")]
  
public DateTime Datapedido { get; set; }

    [DisplayFormat(DataFormatString =
"{0,c}")]
   
public decimal PrecoProduto { get; set; }
}
 

6- DataType - Associa um tipo adicional a uma propriedade

public class Funcionario
{
   [DataType(DataType.DateTime)]
  
public DateTime Nascimento { get; set; }

   [DataType(DataType.Password)]
   [StringLength(10, MinimumLength = 4)]
   [Display(Name =
"Password")]
  
public string? Password { get; set; }

}
 

Neste exemplo, usamos DataType.DateTime, mas há outros tipos de dados que podem ser especificados usando o atributo [DataType] no ASP.NET Core.

E a seguir usamos o [Data Type] Password combinado com outros DAs.

Aqui estão alguns dos mais usados:

DataType.Date, DataType.Time, DataType.Duration, DataType.Password, DataType.MultilineText, DataType.Password, DataType.PhoneNumber, DataType.Url, DataType.Currency …

7- Display

A anotação de dados de Display é usada para definir o nome de exibição de uma propriedade em uma classe de modelo ASP.NET Core. Ela é usada para especificar o nome da propriedade que deve ser exibida nas interfaces do usuário em vez do próprio nome da propriedade.

No exemplo abaixo, indica que a propriedade deve ser exibida como “Nome do Cliente” e 'Nome do Produto' nas interfaces do usuário, ao invés de "Cliente” e 'Produto' :

public class Pedido
{
   [Display(Name =
"Nome do Cliente")]
  
public string? Cliente { get; set; }

   [Display(Name = "Nome do Produto")]
  
public string? Produto { get; set; }
}
 

8- Compare

A anotação Compare é usada para comparar duas propriedades e, geralmente a usamos para confirmar as senhas nas páginas de cadastro.

public class Registro
{
   [Compare(
"Password")]
  
public string ConfirmaPassword { get; set; }
  
public string Password { get; set; }

}
 

9- Remote

A anotação de dados Remote é usada para realizar a validação remota de uma propriedade. Isso pode ser útil para validar a exclusividade, por exemplo.

public class Produto
{
   [Remote(action:
"VerificaNome", controller: "Produtos")]
  
public string Nome { get; set; }
}
 

10- ScaffoldColumn

A anotação de dados ScaffoldColumn é usada para especificar se uma propriedade deve ser incluída nas views com scaffold.

Se você tiver uma página de scaffold que lista os produtos, a propriedade Id será excluída na página de exibição de lista.

public class Produto
{
   [ScaffoldColumn(
false)]
  
public int Id { get; set; }
}
 

11- ForeignKey

A anotação de dados ForeignKey é usada para especificar um relacionamento de chave estrangeira.

public class Pedido
{
   [ForeignKey(
"ClienteId")]
  
public int ClienteId { get; set; }

  
public Cliente Cliente { get; set; }
}
 

12- DisplayColumn

A anotação de dados DisplayColumn é usada para especificar a coluna padrão a ser usada para propósitos de exibição.

[DisplayColumn("NomeCompleto")]
public
class Pessoa
{
  
public string Nome { get; set; }
  
public string Sobrenome { get; set; }
  
public string NomeCompleto => $"{Nome} {Sobrenome}";
}
 

Ao definir o atributo [DisplayColumn(“NomeCompleto”)], você está instruindo o ASP.NET Core a usar a propriedade NomeCompleto como a coluna padrão a ser exibida ao criar views de scaffolding para a classe Pessoa.

13- Bind

A anotação de dados Bind é usada para especificar quais propriedades de um modelo devem ser incluídas na ligação do modelo.

Neste exemplo, apenas as propriedades Nome, Sobrenome e Email serão passadas para o método Create dentro do parâmetro cliente.

public IActionResult Create([Bind("Nome, Sobrenome, Email")] Cliente cliente)
{
 
// ...código
}
 

14- HiddenInput

A anotação de dados HiddenInput é usada para especificar que uma propriedade deve ser renderizada como um campo de entrada oculto.

public class Pedido
{
   [HiddenInput]
  
public int Id { get; set; }
}
 

Neste exemplo, a classe Pedido possui uma propriedade Id decorada com o atributo [HiddenInput], o que significa que ela não será exibida em nenhuma forma gerada pelo ASP.NET Core.

15- DisplayOrder

A anotação de dados DisplayOrder é usada para especificar a ordem na qual as propriedades devem ser exibidas.

public class Produto
{
   [DisplayOrder(1)]
  
public int Id { get; set; }

   [DisplayOrder(2)]
  
public string Nome { get; set; }

}
 

16- TimeStamp

A anotação de dados Timestamp é usada para especificar que uma propriedade deve ser usada para verificação de simultaneidade otimista.

public class Pedido
{
 
public int Id { get; set; }

  [Timestamp]
 
public byte[] Timestamp { get; set; }

 
public string NomeCliente { get; set; }
 
public decimal ValorTotal { get; set; }

}

Neste exemplo, a propriedade Timestamp é decorada com o atributo [Timestamp]. Isso informa ao ASP.NET Core para usar essa propriedade para executar verificações de simultaneidade otimistas ao atualizar registros no banco de dados.

17- DefaultValue

A anotação de dados DefaultValue é usada para especificar um valor padrão para uma propriedade.

public class Produto
{
  [DefaultValue(0)]
 
public int Quantidade { get; set; }
}
 

18- NotMapped

A anotação de dados NotMapped é usada para especificar que uma propriedade não deve ser mapeada para uma coluna do banco de dados.

public class Produto
{
   [NotMapped]
  
public string NomeCompleto => $"{Nome} ({Variacao})";

}

Agora apenas para ilustrar o uso dos atributos Data Annotations vamos definir uma classe Funcionario com diversas propriedades onde iremos usar o atributo pertinente:

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

public class Funcionario
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Required(ErrorMessage = "Nome é obrigatório")]
    [StringLength(50)]
    public string Nome { get; set; }

    [Required(ErrorMessage = "Sobrenome é obrigatório")]
    [StringLength(50)]
    public string Sobrenome { get; set; }

    [DataType(DataType.EmailAddress)]
    [EmailAddress(ErrorMessage = "Endereço de mail inválido")]
    public string Email { get; set; }

    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
    public DateTime Nascimento { get; set; }

    [Range(0, 10, ErrorMessage = "Avalicao deve estar entre 0 e 10")]
    public int Avaliacao { get; set; }

    [RegularExpression("^[0-9]{10}$", ErrorMessage = "Telefone deve ter 10 digitos")]
    public string Telefone { get; set; }

    [CreditCard(ErrorMessage = "Cartão Inválido")]
    public string NumeroCartaoCredido { get; set; }

    [Url(ErrorMessage = "Url do website inválida")]
    public string Website { get; set; }

    [MaxLength(500)]
    public string Biografia { get; set; }

    [MinLength(6, ErrorMessage = "Senha deve ter 6 caracteres")]
    [DataType(DataType.Password)]
    public string Password { get; set; }

    [Compare("Password", ErrorMessage = "Senhas não conferem")]
    [DataType(DataType.Password)]
    public string ConfirmaPassword { get; set; }

    [ForeignKey("Departamento")]
    public int DepartamentoId { get; set; }
    public Departamento Departamento { get; set; }

    [DisplayColumn("NomeCompleto")]
    public string NomeCompleto => $"{Nome} {Sobrenome}";

    [Bind("Nome, Sobrenome, Email")]
    public void UpdateFuncionaio(Funcionario novoFuncionario)
    {
        Nome = novoFuncionario.Nome;
        Sobrenome = novoFuncionario.Sobrenome;
        Email = novoFuncionario.Email;
    }

    [HiddenInput]
    public int CodigoSecreto { get; set; }

    [DisplayOrder(1)]
    public int FuncionarioNumero { get; set; }

    [Timestamp]
    public byte[] Timestamp { get; set; }

    [DefaultValue(10000)]
    public decimal Salario { get; set; }

    [NotMapped]
    public string NomeCompletoEDepartamento => $"{NomeCompleto} ({Departamento.Nome})";
}

Antes de concluir é bom destacar que podemos também usar a Fluent Validation no método OnModelCreating da classe de contexto do projeto para realizar as mesmas tarefas que os atributos Data Annotations fazem.

Os atributos Data Annotations são uma maneira conveniente e declarativa de definir regras de validação diretamente nas propriedades das classes de modelo. Eles fornecem uma forma rápida e simples de adicionar validações básicas, como comprimento máximo de uma string, valores numéricos mínimos/máximos, requerimentos de campos obrigatórios, entre outros.

Por outro lado, a biblioteca Fluent Validation oferece uma abordagem mais flexível e extensível para a validação de modelos e, permite criar validações mais complexas, personalizadas e com lógica condicional. Com a Fluent Validation, é possível definir validações baseadas em várias propriedades ou em regras de negócio específicas. Além disso, a biblioteca possui recursos avançados, como validações assíncronas e a capacidade de agrupar várias regras de validação em um único validador.

A escolha entre Data Annotations e Fluent Validation depende da complexidade e da flexibilidade necessárias para as regras de validação do seu projeto. Se suas validações forem simples e diretas, os atributos Data Annotations podem ser suficientes e mais fáceis de implementar. Por outro lado, se você precisar de validações mais complexas, condicionais ou personalizadas, a Fluent Validation pode ser uma escolha melhor, pois permite maior controle sobre as regras de validação.

Uma prática comum é combinar o uso de atributos Data Annotations com a Fluent Validation, aproveitando os benefícios de ambos. Você pode usar os atributos de Data Annotations para as validações básicas e deixar o Fluent Validation para as validações mais complexas e personalizadas. Isso oferece uma abordagem equilibrada que combina a simplicidade dos atributos Data Annotations com a flexibilidade da Fluent Validation.

E estamos conversados.

"Porque Deus, que disse que das trevas resplandecesse a luz, é quem resplandeceu em nossos corações, para iluminação do conhecimento da glória de Deus, na face de Jesus Cristo"
2 Coríntios 4:6

Referências:


José Carlos Macoratti