ASP.NET Core

Create the initial database schema using EF Core's migration feature

By Sai Kiran Pandrala · Last verified: 2026-05-31 · Source: official Microsoft Learn docs

At a glance
Product familyASP.NET Core
Document sourceAspnet Core Aspnetcore 10.0
Guide typeConfiguration Guide
Skill levelIntermediate to advanced
Time15 - 60 minutes depending on environment

This guide covers Create the initial database schema using EF Core's migration feature on ASP.NET Core end to end. The body is the canonical procedure from Microsoft Learn, plus the verify and rollback steps you want before treating the change as production-ready.

Hands-on walkthrough

Entity Framework Core is the part of ASP.NET Core that lets you ignore SQL until the day SQL ignores you back. Create the initial database schema using EF Core's migration feature is one of those EF Core topics where the docs are correct but easy to misread. I've burned hours on it; you don't have to.

Last week I rebuilt a staging environment after a colleague mis-clicked the wrong tenant. Took me about 40 minutes and one strong cup of filter coffee.

Setup I'm assuming

Walkthrough

  1. Confirm your DbContext is properly registered. In Program.cs you should see something like builder.Services.AddDbContext<SchoolContext>(opt => opt.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))). If the connection string isn't in appsettings.json or User Secrets, EF won't tell you in a friendly way — it'll just hang on first query.
  2. Check your model. Run dotnet ef dbcontext info and look at the entity list. If something is missing, you forgot to add a DbSet<T> property to the context.
  3. Generate a migration. dotnet ef migrations add Init. Takes 5-10 seconds. The output file goes in Migrations/.
  4. Apply it. dotnet ef database update. This runs the Up() method against the database in your connection string. Don't run it against production from your laptop. Ever.
  5. Test the query path. Add a quick controller or minimal API endpoint that returns the first few rows. Hit it with curl or Insomnia. If you get a 500, check the logs for SqlException.
  6. Enable query logging in dev. Add .LogTo(Console.WriteLine, LogLevel.Information) to your UseSqlServer options. Every SQL statement EF generates prints to the console. You will be horrified by what you find. Good.

Performance notes I always check

EF Core makes generating bad SQL easy. Three checks I run on every PR that touches the data layer:

Verification

Rolling back a migration

This is where teams panic. Don't. EF gives you a clean path.

  1. Identify the previous migration. dotnet ef migrations list.
  2. Roll the database back. dotnet ef database update PreviousMigrationName. EF runs the Down() method of every migration between current and target.
  3. Remove the failed migration files. dotnet ef migrations remove. This deletes the latest migration class.
  4. If the migration has already shipped to production and you're past the safe-to-rollback window, you need a forward fix. Generate a new migration that compensates for the broken one. Never edit a shipped migration in place.

Where EF Core bites

Pair this with a proper CI gate. I run a smoke test on every PR, costs me maybe 4 minutes of GitHub Actions time, catches roughly 80% of regressions.

Operational notes

The next section is what I wish someone had handed me three years ago.

Team and runbook. Pair this with code review checklist items. A two-line note in the PR template covers 80% of the recurring miss.

Cost watch. Free-tier limits move. What costs zero today may cost $0.50 per million requests next year. Set a budget alert at 50% of expected monthly spend: costs you nothing, saves you from a surprise bill.

Security pass. Run a quick OWASP check. Inputs, outputs, authentication, authorization, logging. Five minutes of scanning catches the obvious mistakes.

Observability. Add a span. Distributed tracing is cheap to add and expensive to retrofit. System.Diagnostics.Activity is the API; ActivitySource + ActivityListener is the wiring.

Patterns I keep coming back to

Across many projects in ASP.NET Core, a few patterns repeat that are worth naming explicitly.

Decisions worth revisiting periodically

Some choices made early in a project age fast. I keep a list of "look at this every six months" items per project. ASP.NET Core-flavored versions look like this:

  1. SDK and framework version. Are you on the current LTS? Microsoft's support window is fixed; falling behind two majors means you're paying interest in security patches.
  2. Dependency surface. Run dotnet list package --outdated quarterly. Each outdated package is a small risk; cumulatively, large risk.
  3. Build time. If your CI builds creep past 8-10 minutes, the developer feedback loop slows enough to hurt productivity. Profile and trim.
  4. Test coverage and execution time. Coverage that climbs slowly is fine. Test suites that climb slowly toward 30 minutes is a warning sign, refactor or parallelize.
  5. Production error budget. If you're hitting your error budget consistently, slow feature work and pay down operational debt. If you're under-using your error budget, you're shipping too slowly.

Why I do it this way

Years ago I treated each topic in isolation. I'd read the docs, implement the feature, ship it, move on. The result was a lot of features that worked individually and didn't compose well. Today I default to: read the docs, sketch a diagram on paper or a digital whiteboard, identify the trust boundary, identify the failure mode, then implement. Ten extra minutes up front, hours saved down the road.

The other shift was treating tests as the design tool, not the verification tool. Writing the test first forces you to think about the API surface from the caller's perspective. By the time the test compiles, the design has been pressure-tested by your own most demanding consumer: a test that has to drive the feature without knowing the implementation.

Practical example: last month I rewrote a small reporting endpoint. The first draft was a single 60-line action method that pulled data, transformed it, and returned JSON. Tests forced me to split it into an IReportRepository, an IReportFormatter, and a thin controller. Same functionality, but now I can swap the formatter for HTML output by writing one new class and zero changes to the controller. That's the power of writing the test first.

References worth bookmarking

How to apply this in practice

Caveats and what to double-check

FAQ

How long does create the initial database schema using ef core's migration feature typically take?
For most ASP.NET Core environments, 15 to 60 minutes including verification. Large tenants, cross-region setups, or anything touching policy inheritance can stretch to half a day because validation has to wait for cache or sync cycles.
Is there a rollback path?
Yes for most ASP.NET Core changes - export the current config first (az CLI, Get-Az PowerShell, or portal Export Template). A few operations are one-way (storage tier moves, region migration, schema bumps) - check Microsoft Learn for the specific resource type before you commit.
Will this affect dependent services?
Possibly. ASP.NET Core resources are often referenced by other workloads (Entra apps, Logic Apps, Functions, downstream pipelines). Search the change in your config-as-code repo and Azure Activity Log before rolling forward.
What if the documented steps do not match my portal?
Microsoft frequently restructures the ASP.NET Core portal experience. Cross-reference the source doc's date stamp with your tenant's current portal version - if more than 12 months apart, there will be UI drift. The underlying API call usually still works via CLI.
Where do I get help if I am still stuck?
Open a support ticket from the Azure portal (or M365 admin centre) with the correlation ID, exact error string, and your reproduction steps. The ASP.NET Core Tech Community forum is also usable - search for the exact error before posting; 80% of common issues already have answers.

References

Related guides worth a look while you sort this one out: