Want EF Core running in under 10 minutes without guesswork? Follow this path and you’ll have migrations created, a database on disk, and your first query printed to the console.
What you’ll build
We’ll set up a tiny data layer with SQLite (no server needed), create a migration, apply it, and run a query. You’ll also see quick switches for SQL Server and PostgreSQL.
Plan:
- Create a new .NET project.
- Add EF Core packages.
- Write a
DbContextand a simple entity. - Create and apply a migration.
- Insert sample data and run the first query.
- Switch providers (optional) and fix common errors.
I use this exact flow on fresh laptops during workshops. It’s fast, predictable, and easy to repeat.
Prerequisites
- .NET SDK 8+ installed (
dotnet --versionshould print a number). - A terminal (PowerShell, bash, or the VS Terminal).
- Optional: Visual Studio or VS Code.
If you use Visual Studio’s Package Manager Console (PMC), I’ll show the matching commands too.
Step 1 – Create a clean project
You can use a console app or a minimal API. Let’s keep it simple with a console app:
mkdir EfCoreFastStart
cd EfCoreFastStart
dotnet new console -n EfCoreFastStart
cd EfCoreFastStartThe folder now contains Program.cs and a project file EfCoreFastStart.csproj.
Step 2 – Add EF Core packages
For SQLite (a single file database):
dotnet add package Microsoft.EntityFrameworkCore.SqliteFor migrations support (required for CLI migrations):
dotnet add package Microsoft.EntityFrameworkCore.DesignInstall the EF CLI tool once (global):
dotnet tool install --global dotnet-efUsing Visual Studio instead? You can run the same operations from Package Manager Console:
Add-Migration,Update-Database.
Other providers (optional now, details later):
- SQL Server:
dotnet add package Microsoft.EntityFrameworkCore.SqlServer - PostgreSQL:
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
Step 3 – Create the model and context
We’ll track simple products. Create a new folder and two files.
mkdir DataData/Product.cs
namespace EfCoreFastStart.Data;
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
public DateTime CreatedUtc { get; set; } = DateTime.UtcNow;
}Data/ShopContext.cs
using Microsoft.EntityFrameworkCore;
namespace EfCoreFastStart.Data;
public class ShopContext : DbContext
{
public DbSet<Product> Products => Set<Product>();
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
// SQLite: database file will be created next to the EXE
options.UseSqlite("Data Source=shop.db");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>()
.Property(p => p.Name)
.HasMaxLength(200);
modelBuilder.Entity<Product>()
.Property(p => p.Price)
.HasPrecision(18, 2);
}
}Why this design?
OnConfiguringkeeps the sample self‑contained (no DI needed for a console demo).- Precision on
Priceavoids awkward decimals.
Step 4 – Wire the app to apply migrations on startup
Open Program.cs and replace the contents with:
using EfCoreFastStart.Data;
using Microsoft.EntityFrameworkCore;
// Ensure database exists and is up to date
await using var db = new ShopContext();
await db.Database.MigrateAsync();
// Seed a few rows on first run
if (!await db.Products.AnyAsync())
{
db.Products.AddRange(
new Product { Name = "Coffee Beans", Price = 9.99m },
new Product { Name = "Espresso Cup", Price = 4.50m },
new Product { Name = "French Press", Price = 24.90m }
);
await db.SaveChangesAsync();
}
// First query
var cheap = await db.Products
.Where(p => p.Price < 10)
.OrderBy(p => p.Price)
.ToListAsync();
foreach (var p in cheap)
{
Console.WriteLine($"{p.Id}: {p.Name} - {p.Price:C}");
}Note the line await db.Database.MigrateAsync();. This ensures your app applies pending migrations at runtime. During local development it saves time.
Step 5 – Create and apply the first migration
From the project folder (EfCoreFastStart/EfCoreFastStart):
dotnet ef migrations add InitialCreateThis generates a Migrations/ folder with migration files and a model snapshot.
Apply the migration (creates shop.db):
dotnet ef database updateRun the app:
dotnet runExpected output (ids may differ):
1: Espresso Cup - $4.50
2: Coffee Beans - $9.99If you see prices printed, congrats – EF Core is installed, the schema is created, and the first query worked.
Package Manager Console (Visual Studio) equivalents
Add-Migration InitialCreate
Update-DatabaseStep 6 – Change the model and create another migration (optional)
Let’s add a boolean flag and see the migration flow.
Edit Data/Product.cs:
public bool IsActive { get; set; } = true;Create a migration and update the DB:
dotnet ef migrations add AddIsActiveToProduct
dotnet ef database updateThat’s your basic change cycle.
Quick switches: SQL Server and PostgreSQL
SQLite is great for demos and tests. For a real app you may want SQL Server or PostgreSQL.
SQL Server
- Add provider:
dotnet add package Microsoft.EntityFrameworkCore.SqlServer- Update context configuration (ShopContext.cs):
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlServer(
"Server=localhost;Database=ShopDb;Trusted_Connection=True;TrustServerCertificate=True");
}- New migration (if you changed provider on a clean repo you can delete old migrations first):
dotnet ef migrations add InitialCreateSqlServer
dotnet ef database updatePostgreSQL
- Add provider:
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL- Update context configuration:
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseNpgsql("Host=localhost;Database=shopdb;Username=postgres;Password=postgres");
}- Create and apply migrations as usual.
Tip: keep separate appsettings files per environment in real projects and move connection strings out of source code.
Minimal API sample (if you prefer an HTTP endpoint)
If you started with dotnet new web -n EfCoreFastStartApi, wire DI like this:
Program.cs
using EfCoreFastStart.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<ShopContext>(opt =>
opt.UseSqlite(builder.Configuration.GetConnectionString("Default") ?? "Data Source=shop.db"));
var app = builder.Build();
// Apply migrations automatically on startup (dev only)
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<ShopContext>();
db.Database.Migrate();
}
app.MapGet("/products/cheap", async (ShopContext db) =>
await db.Products.Where(p => p.Price < 10).ToListAsync());
app.Run();appsettings.json
{
"ConnectionStrings": {
"Default": "Data Source=shop.db"
}
}Run the app and call /products/cheap.
Troubleshooting (the part that saves hours)
“The term dotnet-ef is not recognized”
- Install the tool:
dotnet tool install --global dotnet-ef. - If already installed, ensure the global tools path is on your
PATH. Restart the terminal after install.
“Unable to create a design-time DbContext”
- EF CLI needs to create your context. With
OnConfiguring, it should work. - If you moved to DI only, add a factory:
using EfCoreFastStart.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
public class ShopContextFactory : IDesignTimeDbContextFactory<ShopContext>
{
public ShopContext CreateDbContext(string[] args)
{
var options = new DbContextOptionsBuilder<ShopContext>()
.UseSqlite("Data Source=shop.db")
.Options;
return new ShopContext();
}
}(Or keep OnConfiguring in the context for demos.)
Build failed during migrations
- Migrations run a build. Fix compile errors first.
“No provider configured”
- Check you called
UseSqlite,UseSqlServer, orUseNpgsqlin configuration.
“Cannot open database” on SQL Server
- Check instance name and trust settings. For local development,
TrustServerCertificate=Trueoften helps.
Migrations folder chaos
- If you are still experimenting, you can delete the
Migrations/folder and the DB file, then start fresh:dotnet ef migrations add InitialCreate→dotnet ef database update.
What did we just gain?
- A repeatable EF Core setup you can drop into any small sample.
- A working migration flow (add, update).
- A sample query with async LINQ.
- Clear switches for popular providers.
This is enough to start a real project without drowning in setup.
FAQ: quick answers for common questions
Microsoft.EntityFrameworkCore.Design?Yes, for migrations created by the CLI. Without it, dotnet ef will complain.
dotnet-ef?Global is fine for a single machine. In teams, consider a local tool manifest so everyone runs the same version:dotnet new tool-manifest
dotnet tool install dotnet-ef
Then call: dotnet tool restore → dotnet ef ....
In dev, yes, it’s handy: Database.Migrate(). In prod, run migrations during deployment, not inside app startup.
It’s fine for small desktop tools and tests. For web apps or heavier data, use SQL Server, PostgreSQL, or another full server engine.
appsettings.json or user secrets. Avoid hardcoding for anything beyond a demo.
That’s a bigger topic (CQRS). Start simple here; split later when you have a concrete need.
For demos, insert after Migrate(). For structured seeding, use modelBuilder.Entity<>().HasData(...) and add a migration.
Set precision in OnModelCreating (as shown). This avoids default mappings that may not match your needs.
Conclusion: you now have EF Core ready to work
You created a project, added packages, generated a migration, and ran a query. That’s the full starter loop. Next, try adding another entity (for example, Order) and a relationship. Or swap to SQL Server and point at a real instance.
I’m curious: where will you use this starter – console tool, API, or a background worker? Tell me in the comments and I’ll add the next steps for the most requested path.
