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. This is 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, and your progress syncs across the web and the CoddyKit app. The C# Academy course includes 4 lessons in total.
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
Learn C# with an AI tutor — free
Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.
- Courses
- 93
- Lessons
- 346
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. 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. The C# Academy course includes 4 lessons in total.
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, so you can start here or from the beginning and move at your own pace. This is lesson 3 of 4.
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