Fix: dotnet ef migrations add falha com 'Unable to create an object of type DbContext'
As ferramentas em tempo de design do EF Core não conseguiram instanciar seu DbContext. Exponha um host com WebApplication.CreateBuilder, aponte para o startup project correto ou implemente IDesignTimeDbContextFactory.
A correção: dotnet ef executa sua aplicação em tempo de design para descobrir o DbContext. Falhou porque o ponto de entrada não retornou um host que pudesse ser inspecionado, ou porque seu DbContext tem parâmetros de construtor que não podem ser resolvidos sem um. Em uma aplicação web, garanta que Program.cs compile e use (ou retorne) um WebApplication. Em uma biblioteca de classes ou projeto de testes, adicione uma implementação de IDesignTimeDbContextFactory<TContext>. Depois execute novamente com --startup-project apontando para o projeto host, não para o projeto de dados.
Unable to create an object of type 'AppDbContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
Este guia foi escrito contra Microsoft.EntityFrameworkCore.Design 11.0.0-preview.4, dotnet-ef 11.0.0-preview.4 e o SDK do .NET 11 preview 4. O mesmo comportamento se aplica até o EF Core 3.1: as regras de descoberta em tempo de design não mudaram de forma desde a introdução do host genérico. Se você ainda está no EF Core 6 ou 8, todas as correções abaixo funcionam, apenas os namespaces mudam ligeiramente.
Como as ferramentas em tempo de design encontram seu DbContext
Quando você executa dotnet ef migrations add Init, a ferramenta não faz uma varredura estática do seu código. Ela compila seu projeto, carrega o assembly resultante e procura por uma de quatro coisas, nesta ordem:
- Uma implementação de
IDesignTimeDbContextFactory<TContext>no startup project. - Um host retornado de
Program.Mainou exposto via o padrão implícito deWebApplication. A ferramenta chamaIHost.Services.GetRequiredService<TContext>()contra ele. - Um
DbContextcom um construtor público sem parâmetros. A ferramenta chamanew TContext()diretamente. - Um
DbContextcomOnConfiguringque não depende de serviços injetados.
Se nenhum produzir uma instância, você recebe o erro Unable to create an object of type 'X'. O hyperlink na mensagem aponta para a documentação de tempo de design, que lista os mesmos quatro caminhos.
Por que isso acontece em uma aplicação web típica
A maioria dos projetos falha no caminho 2. A ferramenta consegue chamar seu Program.cs mas não encontra um host para inspecionar. Três coisas comumente quebram o caminho 2 em 2026:
Program.cscompila oWebApplicationmas sai antes que a ferramenta possa lerServicespor causa da ordem das top-level statements.- O
DbContextestá registrado em um assembly diferente daquele passado como--startup-project. A ferramenta executou o projeto errado. - O construtor do
DbContextrecebe um tipo personalizado (um resolver de tenant, um relógio, um serviço de feature flag) que o container de injeção de dependência não consegue resolver sem queapp.Run()execute de fato.
O primeiro é o assassino silencioso. Com top-level statements, o compilador sintetiza um Program.Main cujo tipo de retorno e instrução final importam para o EF Core. Se app.Run() é a última expressão, a ferramenta lê o host via reflexão sobre a classe sintética Program. Se você envolveu a chamada de run em um condicional, ou se faz um return antecipado, o host nunca chega à ferramenta.
Uma reprodução mínima
Este é o menor projeto que produz o erro. Um projeto WebApi, um DbContext com uma dependência injetada, sem design-time factory.
// AppDbContext.cs - .NET 11, EF Core 11.0.0-preview.4
using Microsoft.EntityFrameworkCore;
public sealed class AppDbContext : DbContext
{
private readonly ITenantResolver _tenant;
public AppDbContext(DbContextOptions<AppDbContext> options, ITenantResolver tenant)
: base(options)
{
_tenant = tenant;
}
public DbSet<Order> Orders => Set<Order>();
}
public interface ITenantResolver { string Current { get; } }
public sealed class Order { public int Id { get; set; } public string TenantId { get; set; } = ""; }
// Program.cs - .NET 11
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<ITenantResolver, HttpHeaderTenantResolver>();
builder.Services.AddDbContext<AppDbContext>(o =>
o.UseSqlServer(builder.Configuration.GetConnectionString("Default")));
var app = builder.Build();
if (args.Contains("--migrate-only"))
{
return; // <-- design-time tool reads this path, never reaches app.Run()
}
app.Run();
Executar dotnet ef migrations add Init contra esse projeto imprime o erro. O registro de ITenantResolver só acontece depois de builder.Build(), mas o return antecipado curto-circuita o Main sintetizado e a inspeção de host do EF Core vê um estado parcialmente inicializado. O código de descoberta também tenta new AppDbContext(), que falha porque o construtor precisa de dois argumentos.
Correção 1 - deixe o host ser descobrível (recomendado para aplicações web)
A correção mais limpa é deixar Program.cs terminar de inicializar o host sem returns antecipados condicionais. A design-time host factory do EF Core usa HostFactoryResolver para percorrer o Program.Main compilado e pegar a referência ao IHost. Qualquer coisa que impeça esse percurso também impede o EF Core de encontrar o contexto.
// Program.cs - .NET 11, EF Core 11.0.0-preview.4
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<ITenantResolver, HttpHeaderTenantResolver>();
builder.Services.AddDbContext<AppDbContext>(o =>
o.UseSqlServer(builder.Configuration.GetConnectionString("Default")));
var app = builder.Build();
app.MapGet("/", () => "ok");
app.Run();
Essa única mudança normalmente é suficiente. Confirme com a flag --verbose:
dotnet ef migrations add Init --verbose
Você deve ver linhas como Finding design-time services..., Using application service provider from Microsoft.Extensions.Hosting.IHostBuilder. e Using DbContext factory 'AppDbContext'. Se --verbose reportar No host builder was found, o caminho 2 ainda está quebrado e você precisa da correção 2 ou da correção 3.
Se você genuinamente precisa de um switch --migrate-only (um runner de console que sai antes de app.Run() em produção), coloque-o depois que o host for construído, mas retorne o host em vez de void, para que o Main sintetizado ainda termine com a referência ao host:
// Program.cs - .NET 11
var app = builder.Build();
app.MapGet("/", () => "ok");
if (args.Contains("--migrate-only"))
{
using var scope = app.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
db.Database.Migrate();
}
app.Run();
A ferramenta em tempo de design ainda vê app.Run() como a instrução terminal e consegue inspecionar app.Services antes de invocá-la.
Correção 2 - aponte para o startup project correto
Uma solution com um projeto Web que referencia uma biblioteca de classes Data é a segunda causa mais comum. As pessoas executam dotnet ef migrations add Init de dentro de Data/, onde o DbContext vive, esperando que a ferramenta use o host registrado em Web. Ela não vai usar. A ferramenta compila o projeto atual (ou o que --project indicar) e procura um host dentro daquele assembly.
# Run from the solution root, EF Core 11.0.0-preview.4 / .NET 11
dotnet ef migrations add Init \
--project src/Data/Data.csproj \
--startup-project src/Web/Web.csproj
--project é onde os arquivos de migração são escritos. --startup-project é onde o host vive. Ambas as flags são obrigatórias quando não são o mesmo projeto. Muitos times criam um alias para isso em Directory.Build.props ou num Makefile para nunca precisar digitar a invocação longa.
Você pode verificar qual assembly a ferramenta carregou de fato com dotnet ef dbcontext info --startup-project src/Web/Web.csproj. Ele imprime o nome do tipo resolvido, o provider e a fonte da connection string. Se info funciona mas migrations add falha, você tem um problema de construtor, não de descoberta: pule para a correção 3.
Correção 3 - implemente IDesignTimeDbContextFactory
Para bibliotecas de classes sem host (o layout típico para uma camada de dados empacotada, um projeto de testes ou um projeto compartilhado hospedado do Blazor WebAssembly), não há Program.Main para inspecionar. Adicione uma factory no mesmo projeto que o DbContext:
// DesignTimeDbContextFactory.cs - .NET 11, EF Core 11.0.0-preview.4
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
public sealed class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>
{
public AppDbContext CreateDbContext(string[] args)
{
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlServer("Server=(localdb)\\MSSQLLocalDB;Database=design-time;Trusted_Connection=True;TrustServerCertificate=True")
.Options;
return new AppDbContext(options, new DesignTimeTenantResolver());
}
private sealed class DesignTimeTenantResolver : ITenantResolver
{
public string Current => "design-time";
}
}
A descoberta do EF Core verifica se existe IDesignTimeDbContextFactory<TContext> antes de percorrer o host, então essa implementação também sobrescreve qualquer outra coisa. Isso a torna a correção mais confiável, mas tem um custo: a connection string é duplicada. Leia-a de appsettings.json se quiser evitar isso:
// EF Core 11.0.0-preview.4 - read connection string from config
public AppDbContext CreateDbContext(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Development"}.json", optional: true)
.AddEnvironmentVariables()
.Build();
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlServer(config.GetConnectionString("Default"))
.Options;
return new AppDbContext(options, new DesignTimeTenantResolver());
}
Um lembrete sobre cópia de arquivo: appsettings.json precisa estar configurado como Copy if newer no projeto que executa a ferramenta, ou o diretório de trabalho não vai contê-lo. Se você passar do erro de descoberta e cair em uma connection string null, essa é a mesma armadilha coberta no artigo canônico sobre o erro No connection string named DefaultConnection.
Correção 4 - a armadilha do contrato de args
Se você já tem uma design-time factory e ainda vê o erro na CI, verifique o parâmetro args. A ferramenta do EF Core passa sua própria lista de argumentos para CreateDbContext(string[] args). Código que confunde isso com os args da aplicação e rejeita flags desconhecidas lançará uma exceção antes de o contexto ser retornado. A ferramenta então reporta esse throw como a falha de descoberta:
// Wrong - throws on EF Core's own args
public AppDbContext CreateDbContext(string[] args)
{
if (args.Length != 2) throw new ArgumentException("expected env and db");
...
}
Ou remova a validação, ou aceite que os args em tempo de design são opacos e baseie a lógica em Environment.GetEnvironmentVariable.
Erros que parecem este mas não são
Could not load file or assembly 'Microsoft.EntityFrameworkCore.Design'. Você esqueceu de adicionar o pacoteMicrosoft.EntityFrameworkCore.Designao startup project. Ele precisa estar referenciado lá mesmo que oDbContextviva em outro lugar, porque a ferramenta o carrega da pasta bin do assembly de inicialização.No project was found. Você executoudotnet efde uma pasta sem.csproj. Execute a partir da raiz do projeto ou passe--project.The command 'dotnet-ef' could not be found. O manifesto local de ferramentas está faltando. Executedotnet new tool-manifestedotnet tool install dotnet-ef --version 11.0.0-preview.4. Fixar a versão importa: umdotnet-efglobal instalado anos atrás vai silenciosamente desencaixar com o runtime.Cannot consume scoped service from singleton. A descoberta funcionou, mas o registro de injeção de dependência está errado. Esse é um erro diferente e a correção de scoped vs singleton lifetime cobre isso.A second operation was started on this context instance. Também é um erro diferente, mas usuários do EF Core o encontram pelo mesmo buraco de coelho de busca. O artigo sobre concorrência de DbContext explica passo a passo.
Um checklist de depuração quando nenhuma das correções funciona
Se você tentou as quatro e a ferramenta ainda não consegue encontrar seu contexto, percorra este checklist em ordem. É a mesma lista que a label de triagem “design-time” do time do EF Core recomenda no GitHub.
dotnet buildé bem-sucedido sem warnings sobre assemblies faltantes. A ferramenta roda contra seu build output, então um build verde é pré-requisito.dotnet ef dbcontext list --startup-project src/Web/Web.csprojimprime o nome do seu contexto. Se isso também falhar, o assembly nunca carregou um contexto. Provavelmente faltaAddDbContext.dotnet ef dbcontext infoimprime o provider e a connection string. Se isso tem sucesso masmigrations addfalha, o construtor do seuDbContextlança uma exceção quando invocado de fato. Adicione log.- O
TargetFrameworkdo startup project bate com o runtime da ferramentadotnet-ef. As ferramentas do EF Core 11 miram .NET 11. Elas não conseguem inspecionar um projeto que mira apenasnetstandard2.0. - O startup project tem tanto
Microsoft.EntityFrameworkCore.Designquanto o pacote do provider (Microsoft.EntityFrameworkCore.SqlServer,Npgsql.EntityFrameworkCore.PostgreSQL, etc.) referenciados. Program.csé o ponto de entrada. Se você tem múltiplos métodosMainou usa configurações deOutputTypeque o escondem, a descoberta falha.
Uma vez que dotnet ef dbcontext info funcione de ponta a ponta, todos os outros comandos vão funcionar. Esse é o melhor smoke test e é mais rápido do que executar uma migração de verdade.
Relacionado
- O fluxo de migração de um passo no EF Core 11 cobre
dotnet ef migrations update --add, o novo comando combinado introduzido para atualizações rotineiras de schema. - Para erros de escopo de injeção de dependência em runtime, veja a correção de serviço scoped a partir de singleton.
- Se
GetConnectionStringretorna null em tempo de design, veja o artigo sobre connection string faltante. - Para testar a camada de dados sem encostar na descoberta em tempo de design, testes de integração com Testcontainers mantém o projeto de testes independente do toolchain de migrações.
- Para aquecer a criação do modelo antes da primeira requisição, como aquecer o modelo do EF Core cobre o problema relacionado de caminho frio.
Comments
Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.