Unable to Create an Object of Type DbContext in EF Core: Causes and Fixes

Search for a command to run...

No comments yet. Be the first to comment.
Tool calling is the moment an LLM stops being a text generator and starts touching your systems. Securing LLM tool calling in ASP.NET Core is therefore not really an AI problem - it is an authorizatio

Retrieval-Augmented Generation lives or dies on one unglamorous step, and it is not the model or the vector database. It is chunking. When you get chunking documents for RAG in .NET wrong, the model r

Most teams reach for caching the moment an API gets slow, wire up Redis or output caching, and move on. But there is an older, lighter mechanism built into HTTP itself that solves two problems at once

If you shipped an AI agent in .NET over the last two years, there is a good chance it runs on Semantic Kernel. That was the right call at the time. But with Microsoft Agent Framework reaching GA in Ap

Coding Droplets
299 posts
Coding Droplets is your go-to resource for .NET and ASP.NET Core development. Whether you're just starting out or building production systems, you'll find practical guides, real-world patterns, and clear explanations that actually make sense.
From beginner-friendly tutorials to advanced architecture decisions. We publish fresh .NET content every day to help you grow at every stage of your career.
You add an entity, run dotnet ef migrations add AddOrders, and the tooling stops dead:
Unable to create an object of type 'AppDbContext'. For the different patterns
supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
If you are hitting "unable to create an object of type DbContext" in EF Core, the frustrating part is that your application runs perfectly. It starts, it serves requests, it queries the database. The failure only happens when the EF Core tools try to build your DbContext at design time, which is a completely different code path from the one your API uses at runtime. In production I have watched teams lose an afternoon to this because they kept debugging the runtime configuration, which was never the problem. This article walks through the six causes I actually see in real .NET 10 and EF Core 10 codebases, in the order worth checking them, plus the one fix that works when nothing else does.
Once the migration is unblocked, the next question is usually how migrations should be applied in a real deployment pipeline rather than from a developer laptop. That whole story, from DbContext configuration and Fluent API mapping through to running MigrateAsync at startup, is what Chapter 3 of the ASP.NET Core Web API: Zero to Production course builds out inside one working codebase, so you see the design-time and runtime halves side by side instead of in isolation.
Some of the fixes below are one-liners, others change how your solution is wired together. If you want the reference implementation with the design-time factory, the multi-project layout, and the migration bundle all connected and running, that lives on Patreon as annotated source you can clone and adapt rather than reassemble from snippets.
It means the EF Core command-line tools could not construct an instance of your DbContext class. Migrations are generated by reading your model, and reading your model requires a live DbContext object. The tools never see your running application, so they try three strategies in a fixed order.
From application services. The tools build and execute your startup project, grab the host's service provider, and resolve the DbContext from it. This is the path a default ASP.NET Core project takes.
From a parameterless constructor. If step 1 fails, the tools look for your derived DbContext type in the target project and try new AppDbContext(). This only works when the context configures itself in OnConfiguring.
From a design-time factory. If a class implementing IDesignTimeDbContextFactory<TContext> exists in the target project or the startup project, the tools skip everything else and use it.
The error message appears when all applicable strategies fail. Critically, the message you see first is the outer one. The real reason is an inner exception the tools swallow by default, which is why the first thing to do is never guess.
Add the --verbose flag. The tools then print the full inner exception chain instead of the generic summary:
dotnet ef migrations add AddOrders --verbose
Nine times out of ten the answer is sitting in that output: a SqlException, a null reference from OnModelCreating, an InvalidOperationException about an unregistered service, or a missing configuration key. Read the innermost exception, not the outermost one. Everything below is easier to diagnose once you have it.
This is the single most common cause in any layered solution. Your DbContext lives in an Infrastructure or Data class library, your host lives in an API project, and you run dotnet ef from the library folder. The tools then treat the class library as both the target project and the startup project. A class library has no host, so strategy 1 never runs, and if your context has no parameterless constructor, strategy 2 fails too.
The tools distinguish two projects. The target project is where migration files get written. The startup project is the one they build and execute to obtain configuration and services. Point them at the right pair explicitly:
dotnet ef migrations add AddOrders \
--project src/Shop.Infrastructure \
--startup-project src/Shop.Api
In the Visual Studio Package Manager Console, the equivalent is setting the API project as the solution's startup project and the Infrastructure project as the Default Project in the console dropdown. Getting only one of those two right is the classic half-fix that leaves the error in place.
If you have more than one DbContext in the solution, add --context AppDbContext as well, otherwise the tools fail with a different but equally unhelpful message about being unable to choose.
This one surprises people. The EF Core tools do not statically analyze your startup project. They run it, up to the point the host is built. Any exception thrown before builder.Build() returns surfaces as the design-time error.
Typical culprits I have hit in production codebases:
A required configuration value read with GetRequiredSection or ["Key"]! that only exists in the deployed environment
An Azure Key Vault or AWS Secrets Manager provider registered at startup that cannot authenticate from a developer machine
Eager validation via ValidateOnStart() on an options type whose values are not present locally
A synchronous health probe or warm-up call in the startup path
The tell is that the verbose output shows an exception that has nothing to do with EF Core. The fix is to make startup survive without external dependencies, usually by supplying local values through User Secrets and keeping fail-fast validation scoped to real environments:
// Only enforce fail-fast validation outside of design time and local dev
if (!builder.Environment.IsDevelopment())
{
builder.Services.AddOptions<PaymentOptions>()
.Bind(builder.Configuration.GetSection("Payments"))
.ValidateDataAnnotations()
.ValidateOnStart();
}
Note that dotnet ef runs your app with ASPNETCORE_ENVIRONMENT resolved the normal way, so it usually picks up Development. You can force a different one by passing arguments through to the app after a -- separator:
dotnet ef database update --startup-project src/Shop.Api -- --environment Staging
If the verbose output contains a line like this, you are looking at a DI problem rather than a tooling problem:
Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContextOptions`1[Shop.Infrastructure.AppDbContext]'
while attempting to activate 'Shop.Infrastructure.AppDbContext'.
Your context has the standard options constructor:
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
}
That constructor is fine. The problem is that nothing registered DbContextOptions<AppDbContext> in the service provider the tools found. Either AddDbContext<AppDbContext> is missing entirely, or it lives in an extension method that the API project never calls, or it is registered conditionally behind an environment check that is false at design time. Register it unconditionally in the startup project's composition root:
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));
A related trap: if you register the context with AddDbContextFactory<T> only, the tools looking for AppDbContext itself may not find what they need. The differences between these registration styles matter more than most teams expect, and I covered them in detail in AddDbContext vs AddDbContextPool vs AddDbContextFactory.
Design time reads configuration from the startup project, not the project you are standing in. If your connection string lives in the API project's User Secrets but you run the command with the class library as the startup project, GetConnectionString("Default") returns null and UseSqlServer(null) throws.
Set the secret against the correct project and confirm it resolves:
dotnet user-secrets set "ConnectionStrings:Default" "Server=localhost;Database=Shop;Trusted_Connection=True;TrustServerCertificate=True" --project src/Shop.Api
dotnet ef dbcontext info --project src/Shop.Infrastructure --startup-project src/Shop.Api
dotnet ef dbcontext info is the cheapest possible smoke test. If it prints your provider and connection details, the design-time path is healthy and any remaining failure is in the model itself.
A global dotnet-ef from an older major version cannot always load a newer EF Core runtime. The symptom is a strange inner exception, often a MissingMethodException or a type-load failure, rather than anything that mentions your code. Check what you actually have installed:
dotnet ef --version
Then align it with the Microsoft.EntityFrameworkCore version your projects reference. On .NET 10 with EF Core 10, that means the 10.x line:
dotnet tool update --global dotnet-ef
On a team, pin it instead of leaving it to whatever each machine happens to have. Declaring dotnet-ef as a local tool in .config/dotnet-tools.json and committing that file means dotnet tool restore gives everyone the identical version, which removes an entire category of "works on my machine" migration failures. This is one of the items on my EF Core migration checklist for production teams.
The design-time services live in a separate package, and it has to be referenced by the startup project. If it is only referenced by the Infrastructure library, the tools will tell you the startup project does not reference Microsoft.EntityFrameworkCore.Design. Add it where it belongs:
dotnet add src/Shop.Api package Microsoft.EntityFrameworkCore.Design
Keep it as a development-only dependency so it does not travel into your published output:
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.*">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
When the tools cannot reach a usable host, stop fighting the host. Implementing IDesignTimeDbContextFactory<TContext> bypasses strategies 1 and 2 entirely and hands EF Core exactly what it needs. Put it in the same project as your DbContext:
public sealed class AppDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>
{
public AppDbContext CreateDbContext(string[] args)
{
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlServer("Server=localhost;Database=Shop;Trusted_Connection=True;TrustServerCertificate=True")
.Options;
return new AppDbContext(options);
}
}
Two things worth knowing before you reach for this. First, the connection string here is used only to build the model and generate migration files, so a local development database is fine. It is not the string your application uses at runtime. Second, because the factory short-circuits the other strategies, it will silently mask genuine startup problems in your API project. That is a real trade-off: I reach for it in class libraries, worker projects, and repositories with no obvious host, and I prefer fixing the startup project directly when one exists.
If you would rather not hardcode anything, read the string from an environment variable inside CreateDbContext and fail loudly when it is absent, so a misconfigured machine produces a clear message instead of a mysterious connection error.
Four habits remove almost all repeat occurrences:
Commit the exact command. Put the full dotnet ef migrations add line with its --project and --startup-project flags into your README or a script. Nobody should be reconstructing it from memory.
Pin the tool. Use a local tool manifest so the tooling version is part of the repository, not part of each developer's machine.
Keep startup dependency-free until the host is built. Anything that needs a network call or a cloud secret belongs behind an environment check or in a hosted service, not in the straight-line path of Program.cs.
Verify with dotnet ef dbcontext info in CI. A ten-second check that the design-time path still resolves catches a broken migration setup before it blocks someone mid-feature.
Worth noting for the near future: EF Core 11, in preview at the time of writing, adds a .config/dotnet-ef.json file where project, startupProject, and context can be declared once for the whole repository. That turns the first habit above into something the tooling enforces rather than something people remember.
Because they use different code paths. Your application configures the DbContext through dependency injection at runtime. The EF Core tools build and execute your startup project separately to obtain a DbContext at design time, and that path can fail on a missing startup-project flag, a startup exception, or a missing configuration value that your deployed environment supplies.
Re-run the command with --verbose. The default output shows only the outer message, while verbose output prints the full inner exception chain. The innermost exception is almost always the actual cause and points directly at the fix.
Not necessarily. If the library is referenced by a host project, passing --startup-project at the host is usually enough and keeps design time consistent with runtime configuration. A design-time factory is the right answer when there is no host at all, or when the host cannot start on a developer machine.
That inner exception means the tools found a service provider but AddDbContext<TContext> was never called on it, or was called conditionally in a branch that is false at design time. Register the context unconditionally in the startup project rather than inside an environment-specific block.
No. It is used only to build the model and generate migration files. Applying migrations to a real database uses the connection supplied at that point, either from your application's runtime configuration, the --connection option, or a migration bundle. Still avoid committing production credentials into a factory class.
The startup project, the one the tools build and run. Referencing it only from the project that contains the DbContext is a common mistake in layered solutions. Mark it with <PrivateAssets>all</PrivateAssets> so it stays a development-time dependency.
For the authoritative details on how the tools resolve your context, see the Microsoft documentation on design-time DbContext creation and the EF Core .NET CLI tools reference.
I'm Celin Daniel, Co-founder of Coding Droplets. I've been building .NET and ASP.NET Core systems in production for 13+ years - APIs, distributed backends, enterprise platforms. Everything I write here comes from real shipping experience: patterns that held up, trade-offs that bit us, and lessons learned the hard way.
GitHub: codingdroplets
YouTube: Coding Droplets
Website: codingdroplets.com