Introdução
Se você já configurou EF Core Migrations em um projeto simples com um único csproj, sabe que é razoavelmente direto: dotnet ef migrations add, dotnet ef database update e pronto. Mas a realidade de projetos corporativos é outra. Sua solução tem múltiplos projetos — API, Domain, IoC, Infra.Data.PostgreSQL, Infra.Bus, Infra.Http, Infra.Dto — e o DbContext não vive no mesmo projeto que o entry point. Agora adicione dotnet secrets para gerenciar connection strings de forma segura, a necessidade de fazer scaffolding de bancos legados que já existem, e um time com 5 ou 10 desenvolvedores criando migrations simultaneamente.
Neste artigo, vamos resolver cada um desses problemas na prática. Vamos configurar migrations em uma solução multi-camada real, usar dotnet user-secrets para connection strings (sem senhas em appsettings.json), fazer reverse engineering de bancos existentes com Scaffold-DbContext, e estabelecer estratégias concretas para evitar conflitos de migrations em times grandes. Se você ainda não conhece o mapeamento avançado com Fluent API que gera essas migrations, recomendo antes a leitura de EF Core 8 Fluent API: Mapeamento, ORM e Desacoplamento.
📦 Código-fonte: A implementação completa deste artigo está no repositório blog-zocateli-sample no GitHub. Clone, explore e adapte ao seu contexto.
Estrutura do Projeto Multi-Camada
Antes de falar de migrations, é essencial entender a estrutura do projeto. É uma solução .NET 8 com separação de responsabilidades em múltiplos csproj:
| |
Ponto crítico: O DbContext vive em Infra.Data.PostgreSQL, mas o entry point (Program.cs) vive em Api. O EF Core CLI precisa de ambos para funcionar — ele precisa do DbContext para gerar migrations e do entry point para resolver connection strings e dependências.
ℹ️ Informação: Essa estrutura segue o padrão de Arquitetura em Camadas / Clean Architecture onde o Domain não tem dependência de infraestrutura. Se você quer entender os padrões arquiteturais por trás dessa organização, veja Arquitetura de Software: GoF, Padrões e Microsserviços.
Pré-requisitos
- .NET 8.0 SDK ou superior
- dotnet-ef tool (EF Core CLI):
| |
- Pacotes NuGet no projeto
Infra.Data.PostgreSQL:
| |
⚠️ Atenção: O pacote
Microsoft.EntityFrameworkCore.Designé obrigatório para que o CLI do EF Core funcione. Ele pode ser adicionado no projeto de dados OU no projeto de startup. Se estiver apenas no startup, use--startup-projectao rodar os comandos.
Configurando Migrations em Multi-Projeto
O Problema
Ao executar dotnet ef migrations add sem configuração adicional, o CLI tenta encontrar o DbContext e as dependências no projeto atual. Num projeto multi-camada, isso falha com erros como:
| |
O CLI precisa de duas informações:
- Onde está o DbContext (
--project) — noInfra.Data.PostgreSQL - Onde está o entry point (
--startup-project) — noApi(para resolver DI e connection string)
Solução 1: Parâmetros do CLI (Rápido)
| |
Funciona, mas é verboso. A cada migration você precisa lembrar dos caminhos.
Solução 2: IDesignTimeDbContextFactory (Recomendado)
Crie uma factory no projeto Infra.Data.PostgreSQL que o CLI usa em design-time — sem depender do Program.cs da API:
| |
Com essa factory, o CLI encontra o DbContext diretamente:
| |
💡 Dica: A
IDesignTimeDbContextFactoryé usada apenas pelo CLI do EF Core (em design-time). Em runtime, oDbContextcontinua sendo resolvido pela injeção de dependências configurada no projeto IoC. As duas vias coexistem sem conflito.
dotnet user-secrets para Connection Strings
Nunca coloque connection strings com senhas em appsettings.json (que vai para o Git). Use dotnet user-secrets para armazenar dados sensíveis localmente.
Configuração Inicial
| |
No projeto Api (entry point), faça o mesmo:
| |
No appsettings.json — Sem Senha
O appsettings.json versionado no Git fica sem dados sensíveis:
| |
Em runtime, o builder.Configuration do ASP.NET Core carrega os user-secrets automaticamente quando IsDevelopment() é true. Em produção, use variáveis de ambiente ou Azure Key Vault.
Executando Migrations com Secrets
Com a IDesignTimeDbContextFactory configurada para ler AddUserSecrets, os comandos do EF Core CLI funcionam direto:
| |
⚠️ Atenção: Cada desenvolvedor do time deve executar
dotnet user-secrets setna máquina dele com as credenciais do banco local. Os user-secrets são armazenados em%APPDATA%\Microsoft\UserSecrets\(Windows) ou~/.microsoft/usersecrets/(Linux/macOS) e nunca vão para o Git.
Scaffolding: Banco de Dados Existente (Reverse Engineering)
Quando você precisa integrar com um banco de dados existente (legado), o EF Core pode gerar automaticamente as entidades e configurações a partir do schema existente. Isso é chamado de scaffolding ou reverse engineering.
Comando Básico
| |
Opções Essenciais do Scaffold
| Parâmetro | Descrição |
|---|---|
--output-dir Entities | Onde gerar as classes de entidade |
--context-dir . | Onde gerar o DbContext |
--context LegadoDbContext | Nome da classe DbContext |
--schema public | Scaffold apenas as tabelas de um schema específico |
--table produtos --table categorias | Scaffold apenas tabelas específicas |
--no-onconfiguring | Não gera OnConfiguring com a connection string hardcoded |
--data-annotations | Usa Data Annotations (default é Fluent API — queremos Fluent API!) |
--force | Sobrescreve arquivos existentes |
--no-pluralize | Não pluraliza nomes de DbSet |
Scaffold com Fluent API (Recomendado)
Por padrão, o scaffold já usa Fluent API (não Data Annotations). Para ter o máximo de controle:
| |
💡 Dica: Após o scaffold, mova as entidades geradas para o projeto Domain e ajuste os namespaces. O scaffold gera tudo no projeto de dados, mas seguindo a arquitetura limpa, as entidades pertencem ao Domain. As configurações Fluent API ficam no projeto de dados.
Scaffold Parcial (Tabelas Específicas)
Para bancos grandes, faça scaffold apenas das tabelas necessárias:
| |
Providers para Scaffold
O provider no comando scaffold corresponde ao banco de dados-alvo:
| Banco | Provider NuGet |
|---|---|
| PostgreSQL | Npgsql.EntityFrameworkCore.PostgreSQL |
| SQL Server | Microsoft.EntityFrameworkCore.SqlServer |
| Oracle | Oracle.EntityFrameworkCore |
| MySQL | Pomelo.EntityFrameworkCore.MySql |
| SQLite | Microsoft.EntityFrameworkCore.Sqlite |
Gerenciando Migrations em Times com Múltiplos Desenvolvedores
Este é um dos maiores desafios práticos do EF Core em projetos corporativos. Quando 3, 5 ou 10 desenvolvedores criam migrations simultaneamente, conflitos são inevitáveis se não houver processo claro.
O Problema: Conflitos de Migration
Imagine dois desenvolvedores trabalhando em branches separadas:
| |
Ambas migrations têm o mesmo snapshot como base (após Migration_001). Quando Dev B faz merge após Dev A, o EF Core detecta que o snapshot está inconsistente e falha.
Estratégia 1: Rebase da Migration (Recomendado)
Quando houver conflito, remova a migration conflitante e recrie-a sobre o estado atual:
| |
Estratégia 2: Merge do Model Snapshot
O arquivo AppDbContextModelSnapshot.cs é o que causa a maioria dos conflitos de merge. Trate-o assim:
| |
Estratégia 3: Convenção de Nomes com Timestamp
Use nomes de migration com timestamp para evitar colisões:
| |
Regras de Ouro Para o Time
Nunca edite uma migration já aplicada ao banco de produção/staging — crie uma nova migration para corrigir.
Faça merge da
mainna sua branch ANTES de criar a migration — garante que o snapshot está atualizado.Uma feature, uma migration — evite múltiplas migrations por PR. Se precisar de ajustes, remova, ajuste a entidade e recrie.
Revise migrations no Pull Request — o SQL gerado (
dotnet ef migrations script) deve ser revisado como qualquer outro código.Mantenha o snapshot no Git — o
AppDbContextModelSnapshot.csDEVE ser versionado. Nunca adicione ao.gitignore.Use
--idempotentpara scripts de produção — gera SQL comIF NOT EXISTS, seguro para rodar múltiplas vezes:
| |
- Comunique no canal do time antes de criar migrations — uma mensagem simples como “vou criar migration para tabela X” evita trabalho duplicado.
⚠️ Atenção: Em times maiores (8+ devs), considere designar uma branch de integração onde todas as migrations são consolidadas antes de irem para
main. Isso reduz conflitos de snapshot drasticamente.
Fluxo Completo: Da Entidade ao Banco
Para consolidar tudo, aqui está o fluxo completo em um projeto multi-camada:
| |
Comandos Essenciais de Referência
| Ação | Comando |
|---|---|
| Criar migration | dotnet ef migrations add Nome --project Infra.Data |
| Aplicar migrations | dotnet ef database update --project Infra.Data |
| Remover última migration | dotnet ef migrations remove --project Infra.Data |
| Listar migrations | dotnet ef migrations list --project Infra.Data |
| Gerar script SQL | dotnet ef migrations script --idempotent --project Infra.Data |
| Script entre migrations | dotnet ef migrations script MigA MigB --project Infra.Data |
| Reverter migration | dotnet ef database update NomeMigAnterior --project Infra.Data |
| Scaffold banco existente | dotnet ef dbcontext scaffold "connstr" Provider --project Infra.Data |
| Verificar pendentes | dotnet ef migrations list --project Infra.Data (mostra Pending) |
Dicas e Boas Práticas
Sempre gere o script SQL antes de aplicar em produção — nunca use
dotnet ef database updatedireto em produção. Gere com--idempotent, revise e aplique via DBA ou pipeline.Use
IDesignTimeDbContextFactory— elimina a dependência do startup project e permite que cada desenvolvedor use seus próprios secrets.Separe migrations por provider — se sua aplicação suporta PostgreSQL e SQL Server, mantenha migrations separadas com
MigrationsAssembly()apontando para projetos diferentes.Não ignore o snapshot — o
AppDbContextModelSnapshot.csé o “estado atual” do modelo para o EF Core CLI. Sem ele, o CLI não consegue calcular o diff para a próxima migration.Automatize com Makefile ou scripts — crie atalhos para os comandos mais usados. Para saber mais sobre automação com Makefile, veja Makefile: Automatizando tarefas para Python, Hugo e Docker.
Revise o SQL gerado em PRs — adicione o output de
dotnet ef migrations scriptcomo comentário no PR para que o time possa revisar o DDL.Use
HasData()com cautela — seed data viaHasData()gera migrations. Para dados grandes ou dinâmicos, prefira scripts SQL separados.Configure retry para
database update— em CI/CD, o banco pode não estar pronto. Use:
| |
Conclusão
Gerenciar EF Core Migrations em projetos multi-camada exige configuração específica, mas a recompensa é uma base de código organizada, segura e sustentável. A IDesignTimeDbContextFactory resolve o problema de design-time em soluções com múltiplos csproj. O dotnet user-secrets mantém connection strings fora do código versionado. O scaffolding permite integrar bancos legados sem reescrever o schema manualmente. E com convenções claras de nomes, rebase de migrations e comunicação no time, é possível escalar para times grandes sem conflitos constantes.
O EF Core CLI é poderoso, mas exige disciplina. Estabeleça as regras do time desde o início do projeto, documente o fluxo de criação de migrations no README, e revise o SQL gerado em todo Pull Request. Para entender como o mapeamento avançado com Fluent API gera essas migrations corretamente, confira EF Core 8 Fluent API: Mapeamento, ORM e Desacoplamento.
Leia Também
- EF Core 8 Fluent API: Mapeamento, ORM e Desacoplamento — mapeamento avançado com Fluent API, todos os tipos de relacionamento, herança e funcionalidades avançadas do EF Core 8+.
- Pipeline de Configuração do .NET 8+: IOptions, Secrets e Docker — como
appsettings.json,dotnet user-secrets, variáveis de ambiente e Docker/Podman secrets se integram no pipeline de configuração do .NET 8+. - Gargalo em Banco de Dados com C# e EF Core: Mensageria e Paginação — como resolver gargalos de escrita e leitura em projetos EF Core.
- Paginação em APIs REST com C# e EF Core 8 — todos os tipos de paginação com EF Core para diferentes bancos.
- Makefile: Automatizando tarefas para Python, Hugo e Docker — automatize tarefas repetitivas como migrations com Makefile.
- Design de API REST: Verbos HTTP e Parameter Binding — boas práticas para a camada de API que consome o EF Core.
Referências
- EF Core — Migrations Overview — documentação oficial sobre criação e aplicação de migrations no EF Core.
- EF Core — Design-time DbContext Creation — como o EF Core CLI encontra o DbContext, incluindo
IDesignTimeDbContextFactory. - EF Core — Reverse Engineering (Scaffold) — documentação oficial sobre scaffold de bancos existentes para gerar entidades e configurações.
- ASP.NET Core — Safe storage of secrets — documentação oficial do
dotnet user-secretspara armazenamento seguro de configurações sensíveis. - EF Core — Applying Migrations — estratégias para aplicar migrations em produção (scripts, bundles, runtime).
- EF Core — Managing Migrations — gerenciamento de migrations existentes (remover, rebase, squash).
- EF Core CLI Reference — referência completa de todos os comandos
dotnet ef. - Npgsql — EF Core Provider for PostgreSQL — documentação do provider PostgreSQL para EF Core.
- Repositório blog-zocateli-sample — Migrations — Código-fonte completo dos exemplos deste artigo
Ao comentar, você concorda com nossa Política de Privacidade, Termos de Uso e Política de Exclusão de Dados.