DEV Community

Cover image for Understanding Entity Framework Core — Part 1: Foundations
Albara fahed Alharissy
Albara fahed Alharissy

Posted on

Understanding Entity Framework Core — Part 1: Foundations

When I started learning Entity Framework Core, I thought the hardest part would be memorizing its APIs.

I was wrong.

The real challenge wasn't learning methods like Where(), Include(), or SaveChanges().

It was understanding what actually happens behind the scenes.

This article summarizes the first stage of my EF Core learning journey and covers the core concepts every .NET developer should understand before writing production code.


What You'll Learn

  • Why EF Core exists
  • What ORM is
  • How EF Core translates LINQ into SQL
  • Deferred Execution
  • Change Tracker
  • EF Core vs ADO.NET
  • Database First vs Code First
  • DbContext fundamentals

Why Does EF Core Exist?

Before Entity Framework Core, many .NET applications used ADO.NET directly.

ADO.NET gives developers full control over database access, but it also requires writing a lot of repetitive code.

For almost every query, developers need to:

  • Open a database connection
  • Write SQL manually
  • Execute commands
  • Read data from a DataReader
  • Map rows into C# objects
  • Close the connection

This approach works well, but as applications grow, maintaining this code becomes increasingly difficult.

Entity Framework Core reduces this boilerplate by allowing developers to work with C# objects while handling much of the mapping behind the scenes.


Understanding ORM

ORM stands for Object-Relational Mapping.

Instead of thinking only in terms of database tables and rows, we work with C# classes and objects.

A simple mapping looks like this:

Database Table  →  C# Class
Database Row    →  Object
Database Column →  Property
Enter fullscreen mode Exit fullscreen mode

This makes applications easier to develop and maintain.

However, an ORM does not replace the need to understand databases.

Knowing SQL, relationships, indexes, and query performance is still essential.


EF Core Is a Translator

One misconception I had was believing EF Core somehow executes my C# code.

It doesn't.

Its primary responsibility is translating LINQ expressions into SQL.

The database engine executes the SQL.

EF Core simply translates the query and maps the returned data back into C# objects.

A simplified pipeline looks like this:

LINQ
   │
   ▼
Expression Tree
   │
   ▼
EF Core
   │
   ▼
Database Provider
   │
   ▼
SQL
   │
   ▼
SQL Server
   │
   ▼
C# Objects
Enter fullscreen mode Exit fullscreen mode

Understanding this pipeline completely changed how I think about writing queries.


Deferred Execution

Another important concept is Deferred Execution.

Consider this query:

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

At this point, nothing has been sent to the database.

EF Core is only building the query.

Execution happens only when the application actually requests the data.

For example:

ToList();
FirstOrDefault();
Count();
Any();
Enter fullscreen mode Exit fullscreen mode

Knowing this helps avoid unnecessary database calls and write more efficient applications.


Change Tracker

One of EF Core's most useful features is the Change Tracker.

Instead of checking every object manually, EF Core tracks the state of each entity.

Possible states include:

  • Added
  • Modified
  • Deleted
  • Unchanged
  • Detached

When SaveChanges() is called, EF Core generates SQL only for tracked entities whose state has changed.

This also explains why changes are sometimes not saved.

If an entity isn't being tracked, EF Core has no way of knowing it has been modified.


EF Core vs ADO.NET

Many beginners ask:

Should I stop using ADO.NET now that EF Core exists?

The answer is No.

Both tools solve different problems.

EF Core ADO.NET
Faster development Full SQL control
Less repetitive code Minimal abstraction
Built-in Change Tracking Highly flexible
Easier maintenance Useful for specialized scenarios

The right choice depends on your application's requirements.


Database First or Code First?

EF Core supports two development approaches.

Database First

  • Start with an existing database
  • Generate entity classes using Scaffolding

Best for:

  • Existing databases
  • Legacy systems
  • Enterprise applications

Code First

  • Start with C# classes
  • Generate the database using Migrations

Best for:

  • New applications
  • Greenfield projects

Neither approach is universally better.

Choose the one that best fits your project.


Understanding DbContext

DbContext is the heart of EF Core.

It is responsible for:

  • Managing database connections
  • Executing queries
  • Tracking entities
  • Saving changes
  • Coordinating communication with the database

Almost every EF Core operation starts with a DbContext instance.


Key Takeaways

During this first stage of learning EF Core, I realized something important.

Learning a framework isn't about memorizing APIs.

It's about understanding:

  • Why it exists
  • Which problems it solves
  • What happens behind the scenes
  • When to use it—and when not to

These concepts provide a much stronger foundation than simply learning syntax.


What's Next?

In Part 2, I'll explore how to query data with LINQ in EF Core, including:

  • Filtering
  • Projection
  • Aggregation
  • Loading related data
  • Joins
  • Pagination

If you're also learning .NET or Entity Framework Core, I'd love to hear about your experience.

Feel free to leave a comment or connect with me.

See you in Part 2 🚀

Top comments (1)

Collapse
 
marcusykim profile image
Marcus Kim

The deferred-execution example-where Where(...) only builds the query and ToList() or Any() actually hits the database-is the point where EF Core stops looking like magic. Pairing that with the Change Tracker states also explains two common production surprises: accidental extra queries and updates that silently fail because an entity is detached. On a real service, I'd make materialization and tracking explicit at repository boundaries; AsNoTracking() for read paths and short-lived DbContext units of work make performance and ownership much easier to reason about.