The primary job of an ORM is to abstract away raw SQL INSERT, UPDATE, and DELETE statements. In EF Core, you perform these operations entirely using C# objects via the DbContext, and the framework translates them into SQL when you commit the transaction.
To insert a new row into the database, you instantiate a new C# object, add it to the corresponding DbSet, and command EF Core to save the changes.
// 1. Instantiate the C# Object
var user = new User { Name = "Sandeep", Email = "sandeep@example.com" };
// 2. Stage the object for insertion (Does NOT execute SQL yet)
_context.Users.Add(user);
// 3. Commit the transaction (Executes 'INSERT INTO Users...')
await _context.SaveChangesAsync();
// Notice: EF Core automatically populates the 'Id' property after insertion!
Console.WriteLine($"Inserted User ID: {user.Id}");
Because EF Core uses a Change Tracker, updating data is incredibly simple. You fetch the object, change a property, and call Save.
// 1. Fetch the existing tracked record
var user = await _context.Users.FindAsync(1);
// 2. Modify the property in C# RAM
user.Email = "new_email@example.com";
// 3. Commit the transaction
// EF Core recognizes only the Email changed, and executes:
// UPDATE Users SET Email = '...' WHERE Id = 1;
await _context.SaveChangesAsync();
To delete a record, you must fetch it from the database first, so EF Core can track it, and then mark it for deletion.
var userToDelete = await _context.Users.FindAsync(5);
if (userToDelete != null)
{
// Stages the object for deletion
_context.Users.Remove(userToDelete);
// Executes 'DELETE FROM Users WHERE Id = 5'
await _context.SaveChangesAsync();
}
Q: "When deleting an entity, why do we have to execute a `SELECT` query via `FindAsync()` just to get the object, so we can then execute a `DELETE` query? Isn't that an incredibly inefficient waste of database operations?"
Architect Answer: "Yes, historically, that was a massive flaw in EF Core. Fetching the entire object into memory just to delete it doubled the network I/O. However, there is a legendary trick called 'Stub Deletion'. If you know the Primary Key (e.g., ID = 5), you can instantiate a fake, empty placeholder object in C#: `var stub = new User { Id = 5 };`. Then, you attach it directly to the context using `_context.Users.Remove(stub);`. EF Core doesn't care that the properties are empty; it only looks at the Primary Key to generate the `DELETE FROM` statement. This allows you to delete records without ever executing the preliminary `SELECT` query. (Note: in .NET 7+, Microsoft finally gave us `.ExecuteDeleteAsync()` to natively bypass this altogether)."