Fluent API no EF Core
Há cenários de mapeamento em que não podem ser alcançados usando Annotationse e por isso o FluentAPI é considerado um recurso mais avançado que o Data Annotations. As diferenças estão nas abordagens, uma vez que a decoração por atributos (Data Annotations) atua sobre classes que representam Models e suas propriedades, enquanto o FluentAPI atua em um nível global, enunciando as regras diretamente no contexto da aplicação. ** Ao invés de colocar atributos (Data Annotations) nas classes... Você configura tudo dentro do OnModelCreating com código fluente. Fluent API não é algo exclusivo do Entity Framework, é um estilo de programação. No EF Core, virou sinônimo de Configurar como as entidades do sistema se comportam no banco de dados... mas via código fluente, não por atributos: Os métodos são encadeados A configuração parece uma “frase” fica legível e descritivo Ordem de prioridade de mapemaentos: Quem ganha? Se tiver conflito: Fluent API Data Annotation Convention (padrão do EF) Quando usar FluentAPI? Podemos considerar utilizar o Fluent em cenários mais específicos, onde requer um controle mais fino sobre o mapeamento entre as entidades e o banco de dados. Geralmente é mais vantajoso em aplicações de grande porte ou de alta complexidade, onde as necessidades específicas de configuração vão além do que as Data Anotations podem oferecer. Exemplos de aplicações Aplicações Empresariais Complexas: onde múltiplas entidades possuem modelos complexos e com um alto nível de inter-relacionamento e heranças. Pode ser necessário para configurar de forma detalhada índices compostos, heranças, propriedade sombreada, validações específicas etc. Tudo isso de forma centralizada e mantidas de forma coesa. Migrações de Sistemas Legados: Quando migramos uma aplicação legada para o EF Core, onde o schema do banco de dados existente deve ser mapeado cuidadosamente para novas classes de domínio. Aplicações com Processo de Negócios Personalizados: Sistemas que têm regras de negócios altamente personalizados, como plataforma de seguros, sistemas de planejamento de recursos empresariais (ERP) ou plataforma de gerenciamento de projeto complexo. Soluções SaaS Personalizáveis: Software como serviço, onde os clientes exigem personalizações significativas em termos de estruturas de dados e comportamento do sistema. Por permitir a criação de dados flexível, diferentes clientes podem ter configurações diferentes, mas ainda assim, serem gerados de uma única base de código. Configuração básica do Fluent API O código do Fluent API fica sempre do método OnModelCreating(ModelBuilder modelbuilder), na classe DBContext. protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity(entity => { entity.HasKey(r => r.Id); // Primary Key entity.Property(r => r.Nome) .IsRequired() // NOT NULL .HasMaxLength(100); // VARCHAR(100) entity.Property(r => r.Faturamento) .HasColumnType("decimal(10,2)"); // Tipo no banco }); } Organização recomendada (boa prática) É uma boa prática na aplicação modularizar, ou seja colocar as configurações de cada Entidade em arquivos próprios. Dessa forma, você mantém o método OnModelCreating limpo, legível e com responsabilidade única: apenas aplicar essas configurações. SeuProjeto/ │ ├── Domain/ │ └── Models/ │ ├── Restaurante.cs │ └── Endereco.cs │ ├── Infra/ │ ├── Context/ │ │ └── EscolaContext.cs │ └── **Configurations/** │ **├── RestauranteConfiguration.cs** │ **└── EnderecoConfiguration.cs** │ └── Program.cs Uma observação interresante é que em sistemas maiores, a modularização pode ser levada ainda mais longe. Pode ser organizada em configurações por áreas funcionais ou módulos, agrupando entidades relacionadas em namespaces ou pastas específicas, por exemplo: └── Data └── Configuration ├── Contas │ └── ContaConfiguration.cs │ ├── Clientes │ └── ClienteConfiguration.cs │ └── Pedidos ├── PedidoConfiguration.cs └── ItemPedidoConfiguration.cs Na pasta RestauranteConfiguration public class RestauranteConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder entity) { entity.ToTable("Restaurantes"); entity.HasKey(r => r.Id); entity.Property(r => r.Nome).IsRequired().HasMaxLength(100); entity.Property(r => r.Faturamento).HasColumnType("decimal(10,2)"); } } No DbContext: protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfiguration(new RestauranteConfiguration()); } Brincando com Fluent API: Cenário real Nosso objetivo é fazer um projeto rápido de um “CRUD”, mas iremos focar apenas na configuração via

Há cenários de mapeamento em que não podem ser alcançados usando Annotationse e por isso o FluentAPI é considerado um recurso mais avançado que o Data Annotations. As diferenças estão nas abordagens, uma vez que a decoração por atributos (Data Annotations) atua sobre classes que representam Models
e suas propriedades, enquanto o FluentAPI atua em um nível global, enunciando as regras diretamente no contexto da aplicação. **
Ao invés de colocar atributos (Data Annotations) nas classes... Você configura tudo dentro do OnModelCreating
com código fluente.
Fluent API não é algo exclusivo do Entity Framework, é um estilo de programação. No EF Core, virou sinônimo de Configurar como as entidades do sistema se comportam no banco de dados... mas via código fluente, não por atributos:
- Os métodos são encadeados
- A configuração parece uma “frase”
- fica legível e descritivo
Ordem de prioridade de mapemaentos: Quem ganha?
Se tiver conflito:
- Fluent API
- Data Annotation
- Convention (padrão do EF)
Quando usar FluentAPI?
Podemos considerar utilizar o Fluent em cenários mais específicos, onde requer um controle mais fino sobre o mapeamento entre as entidades e o banco de dados. Geralmente é mais vantajoso em aplicações de grande porte ou de alta complexidade, onde as necessidades específicas de configuração vão além do que as Data Anotations podem oferecer.
Exemplos de aplicações
- Aplicações Empresariais Complexas: onde múltiplas entidades possuem modelos complexos e com um alto nível de inter-relacionamento e heranças. Pode ser necessário para configurar de forma detalhada índices compostos, heranças, propriedade sombreada, validações específicas etc. Tudo isso de forma centralizada e mantidas de forma coesa.
- Migrações de Sistemas Legados: Quando migramos uma aplicação legada para o EF Core, onde o schema do banco de dados existente deve ser mapeado cuidadosamente para novas classes de domínio.
- Aplicações com Processo de Negócios Personalizados: Sistemas que têm regras de negócios altamente personalizados, como plataforma de seguros, sistemas de planejamento de recursos empresariais (ERP) ou plataforma de gerenciamento de projeto complexo.
- Soluções SaaS Personalizáveis: Software como serviço, onde os clientes exigem personalizações significativas em termos de estruturas de dados e comportamento do sistema. Por permitir a criação de dados flexível, diferentes clientes podem ter configurações diferentes, mas ainda assim, serem gerados de uma única base de código.
Configuração básica do Fluent API
O código do Fluent API fica sempre do método OnModelCreating(ModelBuilder modelbuilder)
, na classe DBContext
.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Restaurante>(entity =>
{
entity.HasKey(r => r.Id); // Primary Key
entity.Property(r => r.Nome)
.IsRequired() // NOT NULL
.HasMaxLength(100); // VARCHAR(100)
entity.Property(r => r.Faturamento)
.HasColumnType("decimal(10,2)"); // Tipo no banco
});
}
Organização recomendada (boa prática)
É uma boa prática na aplicação modularizar, ou seja colocar as configurações de cada Entidade em arquivos próprios. Dessa forma, você mantém o método OnModelCreating
limpo, legível e com responsabilidade única: apenas aplicar essas configurações.
SeuProjeto/
│
├── Domain/
│ └── Models/
│ ├── Restaurante.cs
│ └── Endereco.cs
│
├── Infra/
│ ├── Context/
│ │ └── EscolaContext.cs
│ └── **Configurations/**
│ **├── RestauranteConfiguration.cs**
│ **└── EnderecoConfiguration.cs**
│
└── Program.cs
Uma observação interresante é que em sistemas maiores, a modularização pode ser levada ainda mais longe. Pode ser organizada em configurações por áreas funcionais ou módulos, agrupando entidades relacionadas em namespaces ou pastas específicas, por exemplo:
└── Data
└── Configuration
├── Contas
│ └── ContaConfiguration.cs
│
├── Clientes
│ └── ClienteConfiguration.cs
│
└── Pedidos
├── PedidoConfiguration.cs
└── ItemPedidoConfiguration.cs
Na pasta RestauranteConfiguration
public class RestauranteConfiguration : IEntityTypeConfiguration<Restaurante>
{
public void Configure(EntityTypeBuilder<Restaurante> entity)
{
entity.ToTable("Restaurantes");
entity.HasKey(r => r.Id);
entity.Property(r => r.Nome).IsRequired().HasMaxLength(100);
entity.Property(r => r.Faturamento).HasColumnType("decimal(10,2)");
}
}
No DbContext
:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new RestauranteConfiguration());
}
Brincando com Fluent API: Cenário real
Nosso objetivo é fazer um projeto rápido de um “CRUD”, mas iremos focar apenas na configuração via Fluent API. Nesse projeto vamos focar na organização de pastas padrão de mercado e boas práticas.
1. Estrutura de Pastas Ideal
/ProjetoRestaurante
│
├── Domain
│ ├── Entities
│ │ └── Restaurante.cs
│
├── Infra
│ ├── Data
│ │ ├── Context
│ │ │ └── AppDbContext.cs
│ │ └── Mappings
│ │ └── RestauranteMapping.cs
│
├── Program.cs
├── appsettings.json
└── ProjetoRestaurante.csproj
Entidade (Domain Layer)
namespace ProjetoRestaurante.Domain.Entities;
public class Restaurante
{
public int Id { get; set; }
public string Nome { get; set; }
public string CNPJ { get; set; }
public string Telefone { get; set; }
}
Mapping do Restaurante (Infra Layer)
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using ProjetoRestaurante.Domain.Entities;
namespace ProjetoRestaurante.Infra.Data.Mappings;
public class RestauranteMapping : IEntityTypeConfiguration<Restaurante>
{
public void Configure(EntityTypeBuilder<Restaurante> builder)
{
builder.ToTable("Restaurantes");
builder.HasKey(x => x.Id);
builder.Property(x => x.Id)
.HasColumnName("Id")
.IsRequired();
builder.Property(x => x.Nome)
.HasColumnName("Nome")
.IsRequired()
.HasMaxLength(100);
builder.Property(x => x.CNPJ)
.HasColumnName("CNPJ")
.IsRequired()
.HasMaxLength(14);
builder.Property(x => x.Telefone)
.HasColumnName("Telefone")
.HasMaxLength(20);
}
}
Esse cara tá dizendo assim:
"Ei EF, a configuração de como a entidade Restaurante deve ser mapeada pro banco tá lá na classe RestauranteMapping. Usa ela!"
DBContext (Infra Layer)
using Microsoft.EntityFrameworkCore;
using ProjetoRestaurante.Domain.Entities;
using ProjetoRestaurante.Infra.Data.Mappings;
namespace ProjetoRestaurante.Infra.Data.Context;
public class AppDbContext : DbContext
{
public DbSet<Restaurante> Restaurantes { get; set; }
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new RestauranteMapping());
base.OnModelCreating(modelBuilder);
}
}