O mapeamento de classes no .NET é o processo de associar classes da aplicação a tabelas no banco de dados ou a outros modelos de dados. O conceito de mapeamento vai muito além do banco de dados, ele pode aparecer em diversas áreas como: serialização de dados (JSON, XML), tranformação de dados para APIs (DTOs, ViewModels), etc. Isso pode ser feito de várias formas, dependendo da tecnologia utilizada, como Entity Framework Core, Dapper, AutoMapper, entre outros.
No EF Core, se você definir uma classe como abaixo:
public class Restaurante
{
public int Id { get; set; }
public string Nome { get; set; }
public string Endereco { get; set; }
}
E adicioná-la ao seu DbContext:
public class AppDbContext : DbContext
{
public DbSet<Restaurante> Restaurantes { get; set; }
}
O EF Core automaticamente cria uma tabela chamada Restaurantes com colunas baseadas nas propriedades da classe e você será capaz de rodar a migração para o seu banco deados, portanto, há riscos quando não se aplica os conceitos fundamentais para o mapeamento de classes
O mapeamento de propriedades define características específicas de cada propriedade dentro de uma entidade. Aqui, configuramos detalhes como tamanho, tipo de dado, obrigatoriedade, valores padrão e chaves primárias.
Por que é importante? Quando não mapeamos nosssas propriedades, o EF Core utiliza o usa valores padrão durante a migração, que podem causar problemas de perfomance e estrutura do banco.
string
para NVARCHAR(MAX)
, o que pode causar problemas de performance, já que colunas do tipo MAX
não são armazenadas diretamente na página da tabela, mas sim em uma área separada.decimal
, o EF Core pode usar decimal(18,2)
, o que pode ser insuficiente para cálculos financeiros precisos.IsRequired()
, os campos podem ser criados como NULL no banco.O mapeamento de coluna permite configurar como as propriedades e entidades serão nomeadas e estruturadas no banco de dados
Porque é importante? Sem esse mapeamento, o EF Core usaria o nome da propriedade como nome da coluna no banco, o que pode não seguir os padrões da equipe de DBAs ou dificultar a leitura dos dados.
A configuração de modelagem é um conceito mais amplo e complexo. Envolve aspectos do mapeamento de entidades e conceitos mais profuindos de bancos de dados como:
Tanto Data Annotations quanto FluentAPI permitem aplicar esses três conceitos de mapeamento de classes. Porém, cada um tem suas particularidades. A escolha depende do nível de personalização que você deseja.
De forma geral, vou dar uma breve resumida dos objetivos e comparações, você pode se aprofundar se quiser no material posterior sobre Data Annotations e FluentAPI
Critério | Data Annotations | Fluent API |
---|---|---|
Melhor para | Configurações básicas | Configurações avançadas e personalizadas |
Organização | Mistura regras no model, poluindo classes com atributos | Separa configuração do modelo, mantendo a entidade limpa |
Simplicidade | Mais fácil de aplicar e melhor opção para projetos pequenos | Pode ser verboso e precisa configurar no onModelCreating |
Relacionamentos | ❌ Suporte limitado | ✔ Melhor para relações complexas |
Configuração avançada | ❌ Limitado | ✔ Suporte total |
Se o projeto é pequeno e simples, Data Annotations pode ser suficiente.
Se o projeto é grande ou requer configurações complexas, FluentAPI é a melhor escolha.
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
public class Restaurante
{
[Key] // Define a chave primária
public int Id { get; set; }
[Required] // Define como obrigatório
[MaxLength(100)] // Limita o tamanho do campo
public string Nome { get; set; }
[Column(TypeName = "decimal(10,2)")] // Define tipo específico no banco
public decimal Faturamento { get; set; }
}
OnModelCreating
no DbContext
.public class RestauranteConfiguration : IEntityTypeConfiguration<Restaurante>
{
public void Configure(EntityTypeBuilder<Restaurante> builder)
{
builder.HasKey(r => r.Id); // Define a chave primária
builder.Property(r => r.Nome)
.IsRequired()
.HasMaxLength(100); // Define obrigatoriedade e tamanho máximo
builder.Property(r => r.Faturamento)
.HasColumnType("decimal(10,2)"); // Define o tipo no banco
}
E depois, no DbContext
:
public class MeuDbContext : DbContext
{
public DbSet<Restaurante> Restaurantes { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new RestauranteConfiguration());
}
}