Entity Framework Core for .NET Developers — From Zero to First Query
Entity Framework Core (EF Core) is a powerful Object–Relational Mapper (ORM) for .NET developers.
Instead of hand‑writing SQL everywhere, you work with C# classes and LINQ, and EF Core translates that into SQL for you.
If you’re starting to build apps that talk to a database — Web APIs, background workers, desktop apps, or even small console tools — EF Core gives you:
- Strongly‑typed access to data
- Cross‑platform support (Windows, macOS, Linux)
- Migrations to evolve the schema over time
- A rich LINQ surface for querying
In this post we’ll build a complete console app that:
- Defines a
Studententity - Uses
DbContextto talk to a SQL Server database - Creates the database using migrations
- Performs basic CRUD (Create, Read, Update, Delete) operations
You can treat this as your first EF Core lab and expand it later into a Web API or a bigger project.
Table of Contents
- What Is Entity Framework Core?
- How EF Core Thinks: Model, Context, Provider
- Creating a Console Project and Installing EF Core
- Defining Your First Entity:
Student - Creating the
AppDbContext - Enabling and Running Migrations
- Writing Your First EF Core Program (Add + Query)
- Common CRUD Operations in EF Core
- When and Why to Use Migrations
- Next Steps: Relationships, Loading Strategies & Beyond
1. What Is Entity Framework Core?
Entity Framework Core (EF Core) is an ORM framework by Microsoft that lets you:
- Map C# classes to database tables
- Map properties to columns
- Use LINQ to query the database
- Track changes on entities and persist them with
SaveChanges()
Instead of this:
SELECT Id, Name, Age FROM Students WHERE Age > 20;
…you write this:
var students = context.Students
.Where(s => s.Age > 20)
.ToList();
EF Core figures out the SQL, sends it to the database, and materializes the result as a list of Student objects.
Key features at a glance
- Cross-platform: Works on Windows, macOS, Linux.
- LINQ everywhere: Query with strongly typed C# expressions.
- Change tracking: EF knows which entities changed and generates SQL for you.
- Migrations: Keep your schema in sync with your C# models over time.
- Provider model: Use SQL Server, PostgreSQL, MySQL, SQLite, etc., by changing the provider.
2. How EF Core Thinks: Model, Context, Provider
There are three core concepts to keep in your head:
2.1 Model (Entities)
Your C# classes represent the database structure:
- Class = table
- Property = column
- Navigation property = relationship (foreign key)
2.2 Context (DbContext)
DbContext represents a session with the database.
It’s responsible for:
- Opening connections and executing queries
- Tracking changes to your entities
- Grouping updates into a unit of work (
SaveChanges)
2.3 Provider
EF Core supports multiple databases through providers:
Microsoft.EntityFrameworkCore.SqlServerNpgsql.EntityFrameworkCore.PostgreSQLPomelo.EntityFrameworkCore.MySqlMicrosoft.EntityFrameworkCore.Sqlite- …and more
Switching providers is mostly a matter of changing one line in OnConfiguring or in your DI configuration.
3. Creating a Console Project and Installing EF Core
Let’s start with a clean console app.
3.1 Create the project
dotnet new console -n EFCoreExample
cd EFCoreExample
3.2 Install EF Core packages
For SQL Server we need:
-
Microsoft.EntityFrameworkCore— the core EF library -
Microsoft.EntityFrameworkCore.SqlServer— the SQL Server provider -
Microsoft.EntityFrameworkCore.Tools— CLI tools for migrations
Install them:
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
💡 If you’re using another database, swap the provider package (e.g., SQLite, PostgreSQL). The rest of the code stays almost the same.
4. Defining Your First Entity: Student
Create a file Student.cs:
namespace EFCoreExample;
public class Student
{
public int Id { get; set; }
public string Name { get; set; } = default!;
public int Age { get; set; }
}
EF Core will:
- Create a table named
Students(pluralized by convention) - Map
Idas the primary key - Map
NameandAgeto columns
✅ Conventions first: EF Core has sensible defaults, and you can override them later with Fluent API or Data Annotations.
5. Creating the AppDbContext
Now we create the bridge between our code and the database.
Add AppDbContext.cs:
using Microsoft.EntityFrameworkCore;
namespace EFCoreExample;
public class AppDbContext : DbContext
{
public DbSet<Student> Students => Set<Student>();
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// LocalDB for development. Adjust as needed.
optionsBuilder.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=EFCoreExampleDB;Trusted_Connection=True;");
}
}
What’s happening here?
-
DbSet<Student> Studentstells EF Core: “track and queryStudententities here”. -
UseSqlServer(...)configures the database provider and connection string.
🔐 Production tip: don’t hardcode connection strings. Use
appsettings.jsonor environment variables in real apps. For a learning console app, this is fine.
6. Enabling and Running Migrations
Now we want EF Core to create the actual database and table that match our model.
6.1 Install design package (for tools)
The tools rely on the design-time package:
dotnet add package Microsoft.EntityFrameworkCore.Design
6.2 Add the first migration
dotnet ef migrations add InitialCreate
This command:
- Scans your
DbContextandStudententity - Generates a migration class under a
Migrations/folder - Describes how to create the schema (e.g., create the
Studentstable)
6.3 Apply the migration to the database
dotnet ef database update
EF Core connects to EFCoreExampleDB and executes the migration, creating:
- The database (if it doesn’t exist)
- The
Studentstable
🧪 Want to verify? Open SQL Server Management Studio or Azure Data Studio and inspect the database.
You should see__EFMigrationsHistoryandStudentstables.
7. Writing Your First EF Core Program (Add + Query)
Now that the schema exists, we’ll use EF Core to insert and read data.
Edit Program.cs:
using System;
using System.Linq;
namespace EFCoreExample;
class Program
{
static void Main(string[] args)
{
using var context = new AppDbContext();
// 1) Insert a student if not present
if (!context.Students.Any(s => s.Name == "John Doe"))
{
var student = new Student
{
Name = "John Doe",
Age = 20
};
context.Students.Add(student);
context.SaveChanges();
}
// 2) Query the student
var query = context.Students
.Where(s => s.Name == "John Doe");
foreach (var stud in query)
{
Console.WriteLine($"Student: {stud.Name}, Age: {stud.Age}");
}
}
}
Run it:
dotnet run
You should see:
Student: John Doe, Age: 20
Every time you run it now, the guard clause (Any) prevents inserting duplicates.
8. Common CRUD Operations in EF Core
Let’s quickly summarize the most common operations you’ll use in real projects.
8.1 Insert (Create)
var student = new Student { Name = "Alice", Age = 22 };
context.Students.Add(student);
context.SaveChanges();
8.2 Read (Query)
All students:
var students = context.Students.ToList();
Filtered:
var olderStudents = context.Students
.Where(s => s.Age > 20)
.ToList();
Single by key:
var student = context.Students.Find(1); // Uses primary key
8.3 Update
var alice = context.Students.First(s => s.Name == "Alice");
alice.Age = 23;
context.SaveChanges();
EF Core tracks that Age changed and generates the appropriate UPDATE statement.
8.4 Delete
var toDelete = context.Students.First(s => s.Name == "Alice");
context.Students.Remove(toDelete);
context.SaveChanges();
💡 Under the hood,
Removemarks the entity asDeleted;SaveChangessends theDELETESQL.
9. Migrations: Evolving the Schema Safely
As your app grows, your model will change:
- You add properties (
BirthDate,IsActive) - You introduce relationships (
Course,Enrollment) - You rename or split entities
With EF Core migrations you can version your schema in code.
9.1 Example: Adding a property
Say we add a BirthDate property:
public class Student
{
public int Id { get; set; }
public string Name { get; set; } = default!;
public int Age { get; set; }
public DateTime? BirthDate { get; set; } // new
}
Then:
dotnet ef migrations add AddBirthDateToStudent
dotnet ef database update
EF will generate and apply the ALTER TABLE statements needed to evolve the schema.
🧠 Pro tip: commit your migrations into source control. They are part of your app’s history.
10. Next Steps: Going Beyond the Basics
You now have a working mental model and a running project:
- Entity →
Student - Context →
AppDbContext - Provider → SQL Server via
UseSqlServer - Migrations →
InitialCreate,AddBirthDateToStudent - CRUD → Insert, Query, Update, Delete
Here’s where to go next.
10.1 Relationships
Learn how to model:
- One‑to‑many (e.g.,
Student→Enrollment) - Many‑to‑many (e.g.,
Student↔Course) - One‑to‑one (e.g.,
Student↔StudentProfile)
10.2 Loading strategies
Understand:
-
Eager loading with
.Include(...) - Lazy loading (where supported)
-
Explicit loading with
Entry().Collection().Load()
10.3 Performance & best practices
- Use
AsNoTracking()for read‑only queries - Keep
DbContextlifetime scoped (short‑lived, per request) - Avoid chatty queries; use projections and filtering
10.4 Integrating with ASP.NET Core
The console example maps 1:1 to an ASP.NET Core Web API:
- Register
AppDbContextin DI (builder.Services.AddDbContext<AppDbContext>(...)) - Inject
AppDbContextinto controllers / minimal API handlers - Reuse the same entities & migrations
Conclusion
Entity Framework Core is a fantastic tool for .NET developers who need to interact with relational databases without drowning in SQL.
With just a few building blocks:
- Entities (
Student) -
DbContext(AppDbContext) - Providers (
UseSqlServer) - Migrations (
dotnet ef migrations ...)
…you can go from empty folder to fully working data access layer in minutes.
Now that you’ve seen EF Core end‑to‑end in a console app, try:
- Adding a second entity with a relationship
- Exposing data via a minimal API or MVC controller
- Experimenting with different providers (SQLite is great for quick prototypes)
Happy coding, and happy EF Core! 🚀
✍️ Written by: Cristian Sifuentes — .NET / EF Core / Clean Architecture enthusiast.

Top comments (0)