CRUD Operations Overview
CRUD stands for Create, Read, Update, Delete -- the four fundamental operations for working with data. In EF Core, you perform these operations using C# methods on your DbContext and DbSet, and then persist changes by calling SaveChanges.
The Core Workflow
Every data operation in EF Core follows the same pattern:
- Get a DbContext instance (via dependency injection or manual creation)
- Perform operations on entities through DbSet properties
- Call SaveChanges to persist modifications to the database
csharp1using var context = new AppDbContext();23// Create4var product = new Product { Name = "Widget", Price = 9.99m };5context.Products.Add(product);67// SaveChanges generates and executes the SQL8await context.SaveChangesAsync();
EF Core does not immediately execute SQL when you call Add, Remove, or modify a property. Instead, the Change Tracker records your intent, and SaveChanges translates all pending changes into SQL statements in a single batch.
The Key Methods
Here is a summary of the primary EF Core methods for each CRUD operation:
| Operation | Methods | What It Does |
|---|---|---|
| Create | Add, AddRange, AddAsync | Marks entities as Added in the Change Tracker |
| Read | Find, FirstOrDefault, SingleOrDefault, Where, ToList | Queries the database and returns entities |
| Update | Modify tracked properties, Update, Attach | Marks entities or properties as Modified |
| Delete | Remove, RemoveRange, ExecuteDelete | Marks entities as Deleted or performs bulk delete |
| Persist | SaveChanges, SaveChangesAsync | Generates SQL and executes all pending changes |
How the Change Tracker Fits In
The Change Tracker is central to CRUD operations. When EF Core loads an entity from the database, it takes a snapshot of the original values. When you call SaveChanges, it compares current values to the snapshot and generates the appropriate SQL:
1Entity loaded → Original snapshot stored2 │3Properties modified in C#4 │5SaveChanges() called6 │7Change Tracker compares current vs. original8 │9SQL generated only for changed properties10 │11Database updated
This means you do not need to tell EF Core which properties changed. It detects the differences automatically.
Entity States
Every entity tracked by EF Core is in one of these states:
| State | Meaning | SaveChanges Behavior |
|---|---|---|
| Added | New entity, not yet in the database | Generates INSERT |
| Unchanged | Loaded from DB, no modifications | No SQL generated |
| Modified | Loaded from DB, properties changed | Generates UPDATE |
| Deleted | Marked for removal | Generates DELETE |
| Detached | Not tracked by the context | Ignored by SaveChanges |
You can inspect an entity's state at any time:
csharp1var entry = context.Entry(product);2Console.WriteLine(entry.State); // Added, Modified, etc.
Async vs. Sync Methods
EF Core provides both synchronous and asynchronous versions of most methods:
| Sync | Async | Use When |
|---|---|---|
Add() | AddAsync() | AddAsync is needed only for value generators that require DB access |
SaveChanges() | SaveChangesAsync() | Always prefer async in web apps |
Find() | FindAsync() | Always prefer async in web apps |
ToList() | ToListAsync() | Always prefer async in web apps |
In practice, always use async methods in ASP.NET Core and other I/O-bound applications. The synchronous versions are mainly useful in console scripts or tests.
SaveChanges as a Unit of Work
A key concept in EF Core is that SaveChanges commits all pending changes as a single unit of work. If you add two entities and modify a third, all three operations execute within an implicit database transaction:
csharp1context.Products.Add(product1);2context.Products.Add(product2);3existingProduct.Price = 19.99m;45// All three changes are saved in one transaction6await context.SaveChangesAsync();
If any operation fails, the entire batch is rolled back and no partial changes are persisted. This guarantees data consistency without requiring you to manage transactions manually for most scenarios.
Summary
- CRUD operations are performed through DbSet methods on a DbContext
- The Change Tracker records all pending changes until SaveChanges is called
- SaveChanges batches all changes into a single database transaction
- Every tracked entity has a state (Added, Modified, Deleted, Unchanged, Detached)
- Prefer async methods in web applications and I/O-bound code
In the following presentations, we will explore each CRUD operation in detail with practical code examples.