DEV Community

Cristian Sifuentes
Cristian Sifuentes

Posted on

Entity Framework Core for .NET Developers — From Zero to First Query

Entity Framework Core for .NET Developers — From Zero to First Query

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:

  1. Defines a Student entity
  2. Uses DbContext to talk to a SQL Server database
  3. Creates the database using migrations
  4. 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

  1. What Is Entity Framework Core?
  2. How EF Core Thinks: Model, Context, Provider
  3. Creating a Console Project and Installing EF Core
  4. Defining Your First Entity: Student
  5. Creating the AppDbContext
  6. Enabling and Running Migrations
  7. Writing Your First EF Core Program (Add + Query)
  8. Common CRUD Operations in EF Core
  9. When and Why to Use Migrations
  10. 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;
Enter fullscreen mode Exit fullscreen mode

…you write this:

var students = context.Students
    .Where(s => s.Age > 20)
    .ToList();
Enter fullscreen mode Exit fullscreen mode

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.SqlServer
  • Npgsql.EntityFrameworkCore.PostgreSQL
  • Pomelo.EntityFrameworkCore.MySql
  • Microsoft.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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

💡 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; }
}
Enter fullscreen mode Exit fullscreen mode

EF Core will:

  • Create a table named Students (pluralized by convention)
  • Map Id as the primary key
  • Map Name and Age to 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;");
    }
}
Enter fullscreen mode Exit fullscreen mode

What’s happening here?

  • DbSet<Student> Students tells EF Core: “track and query Student entities here”.
  • UseSqlServer(...) configures the database provider and connection string.

🔐 Production tip: don’t hardcode connection strings. Use appsettings.json or 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
Enter fullscreen mode Exit fullscreen mode

6.2 Add the first migration

dotnet ef migrations add InitialCreate
Enter fullscreen mode Exit fullscreen mode

This command:

  • Scans your DbContext and Student entity
  • Generates a migration class under a Migrations/ folder
  • Describes how to create the schema (e.g., create the Students table)

6.3 Apply the migration to the database

dotnet ef database update
Enter fullscreen mode Exit fullscreen mode

EF Core connects to EFCoreExampleDB and executes the migration, creating:

  • The database (if it doesn’t exist)
  • The Students table

🧪 Want to verify? Open SQL Server Management Studio or Azure Data Studio and inspect the database.

You should see __EFMigrationsHistory and Students tables.


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}");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Run it:

dotnet run
Enter fullscreen mode Exit fullscreen mode

You should see:

Student: John Doe, Age: 20
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

8.2 Read (Query)

All students:

var students = context.Students.ToList();
Enter fullscreen mode Exit fullscreen mode

Filtered:

var olderStudents = context.Students
    .Where(s => s.Age > 20)
    .ToList();
Enter fullscreen mode Exit fullscreen mode

Single by key:

var student = context.Students.Find(1); // Uses primary key
Enter fullscreen mode Exit fullscreen mode

8.3 Update

var alice = context.Students.First(s => s.Name == "Alice");
alice.Age = 23;
context.SaveChanges();
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

💡 Under the hood, Remove marks the entity as Deleted; SaveChanges sends the DELETE SQL.


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
}
Enter fullscreen mode Exit fullscreen mode

Then:

dotnet ef migrations add AddBirthDateToStudent
dotnet ef database update
Enter fullscreen mode Exit fullscreen mode

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., StudentEnrollment)
  • Many‑to‑many (e.g., StudentCourse)
  • One‑to‑one (e.g., StudentStudentProfile)

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 DbContext lifetime 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 AppDbContext in DI (builder.Services.AddDbContext<AppDbContext>(...))
  • Inject AppDbContext into 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)