CRUD Operations with EF Core
Perform Create, Read, Update, and Delete operations using EF Core's change tracking and SaveChanges.
CRUD Operations with EF Core is a free C# Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the C# Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
CRUD Overview in EF Core
EF Core handles all four CRUD operations — Create, Read, Update, Delete — through a combination of change tracking and SaveChangesAsync(). Understanding the change tracker is the key to mastering CRUD.
Create: Adding a New Entity
Use DbSet.Add() or AddAsync() to mark an entity as Added. On SaveChangesAsync(), EF Core generates an INSERT and populates the auto-increment key.
var order = new Order
{
CustomerId = 5,
PlacedAt = DateTime.UtcNow,
Status = OrderStatus.Pending
};
await _db.Orders.AddAsync(order);
await _db.SaveChangesAsync();
Console.WriteLine(order.Id); // populated by DBAdding a Graph of Entities
Adding a parent with children in a single call inserts the entire object graph. EF Core resolves the FK relationships automatically.
var order = new Order
{
CustomerId = 5,
PlacedAt = DateTime.UtcNow,
Lines = new List<OrderLine>
{
new() { ProductId = 10, Quantity = 2, Price = 49.99m },
new() { ProductId = 11, Quantity = 1, Price = 19.99m }
}
};
_db.Orders.Add(order);
await _db.SaveChangesAsync();
// Inserts Order + 2 OrderLines in one transactionRead: Querying with LINQ
EF Core translates LINQ expressions to SQL. Compose queries using Where, Select, OrderBy etc., then materialize with ToListAsync or FirstOrDefaultAsync.
// Read all pending orders for a customer
var orders = await _db.Orders
.Where(o => o.CustomerId == 5 && o.Status == OrderStatus.Pending)
.OrderByDescending(o => o.PlacedAt)
.Select(o => new OrderSummaryDto
{
Id = o.Id,
Total = o.Lines.Sum(l => l.Quantity * l.Price),
Status = o.Status.ToString()
})
.ToListAsync();Read: Single Entity Lookups
For single-entity reads, use FindAsync (hits tracker first) or FirstOrDefaultAsync / SingleOrDefaultAsync with a predicate.
// By PK (checks cache first)
var order = await _db.Orders.FindAsync(orderId);
// By predicate (always hits DB)
var latest = await _db.Orders
.Where(o => o.CustomerId == 5)
.OrderByDescending(o => o.PlacedAt)
.FirstOrDefaultAsync();Update: Change Tracking
Entities loaded via EF Core are tracked. Modify properties directly and call SaveChangesAsync(). EF Core detects the diff and generates a minimal UPDATE statement.
var order = await _db.Orders.FindAsync(orderId);
if (order is null) return;
order.Status = OrderStatus.Shipped;
order.ShippedAt = DateTime.UtcNow;
await _db.SaveChangesAsync();
// UPDATE Orders SET Status=..., ShippedAt=... WHERE Id=orderIdUpdate: Bulk Update (EF Core 7+)
ExecuteUpdateAsync updates rows directly in the database without loading entities. It's far more efficient for bulk operations.
// Mark all overdue orders as Cancelled — no loading needed
await _db.Orders
.Where(o => o.Status == OrderStatus.Pending
&& o.PlacedAt < DateTime.UtcNow.AddDays(-30))
.ExecuteUpdateAsync(set => set
.SetProperty(o => o.Status, OrderStatus.Cancelled)
.SetProperty(o => o.UpdatedAt, DateTime.UtcNow));Delete: Removing a Single Entity
Call Remove() on a tracked entity and then SaveChangesAsync(). If you only have the ID, find it first or use ExecuteDeleteAsync.
var order = await _db.Orders.FindAsync(orderId);
if (order is null) return;
_db.Orders.Remove(order);
await _db.SaveChangesAsync();
// DELETE FROM Orders WHERE Id=orderIdDelete: Bulk Delete (EF Core 7+)
ExecuteDeleteAsync issues a single DELETE SQL statement without loading entities — ideal for batch cleanup operations.
// Delete all cancelled orders older than 1 year
int deleted = await _db.Orders
.Where(o => o.Status == OrderStatus.Cancelled
&& o.PlacedAt < DateTime.UtcNow.AddYears(-1))
.ExecuteDeleteAsync();
Console.WriteLine($"Deleted {deleted} old orders");Transactions
All operations within a single SaveChangesAsync call are wrapped in a transaction. For multi-step operations across multiple saves, use an explicit transaction.
using var transaction = await _db.Database.BeginTransactionAsync();
try
{
_db.Orders.Add(newOrder);
await _db.SaveChangesAsync();
inventory.Stock -= newOrder.Quantity;
await _db.SaveChangesAsync();
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}Real-World: Full CRUD Service
A complete service wraps all CRUD operations with proper error handling and returns DTOs to avoid exposing entity internals.
public async Task<OrderDto> CreateOrderAsync(CreateOrderRequest req)
{
var order = new Order { CustomerId = req.CustomerId, PlacedAt = DateTime.UtcNow };
_db.Orders.Add(order);
await _db.SaveChangesAsync();
return new OrderDto { Id = order.Id, Status = order.Status.ToString() };
}
public async Task<bool> CancelOrderAsync(int id)
{
int rows = await _db.Orders
.Where(o => o.Id == id && o.Status == OrderStatus.Pending)
.ExecuteUpdateAsync(s =>
s.SetProperty(o => o.Status, OrderStatus.Cancelled));
return rows > 0;
}Quick Check
What is the advantage of ExecuteUpdateAsync / ExecuteDeleteAsync over loading entities and calling SaveChangesAsync?
Recap: CRUD Operations with EF Core
Key takeaways:
- Add + SaveChangesAsync → INSERT; EF Core populates auto-generated keys
- Modify tracked entities + SaveChangesAsync → minimal UPDATE
- Remove + SaveChangesAsync → DELETE for single entities
- ExecuteUpdateAsync / ExecuteDeleteAsync → bulk operations without loading (EF Core 7+)
- Wrap multi-step operations in an explicit transaction for atomicity
Frequently asked questions
Is the “CRUD Operations with EF Core” lesson free?
Yes — the full text of “CRUD Operations with EF Core” is free to read here on the web, and the C# Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the C# Academy course, upgrade to CoddyKit PRO.
What will I learn in “CRUD Operations with EF Core”?
Perform Create, Read, Update, and Delete operations using EF Core's change tracking and SaveChanges. You practise C# Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start C# Academy?
No prior experience is required. C# Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “CRUD Operations with EF Core” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this C# Academy lesson?
Yes. Every C# Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- DbContext & DbSet Basics
- Migrations & Schema Management
- CRUD Operations with EF Core
- Relationships: One-to-Many & Many-to-Many