Saving Data with Entity Framework Core
Entity Framework Core (EF Core) simplifies data persistence by allowing you to work with your database using .NET objects. Saving data involves adding new entities, modifying existing ones, and deleting entities, all managed through the DbContext
.
Adding New Entities
To add a new entity, you first create an instance of your entity class and then attach it to the DbContext
using the Add()
method. EF Core tracks this entity as "Added". When SaveChanges()
is called, EF Core generates the appropriate SQL INSERT statement.
Example: Adding a New Blog
using (var db = new BloggingContext())
{
var newBlog = new Blog { Url = "https://example.com/blog" };
db.Blogs.Add(newBlog); // Mark the entity as Added
await db.SaveChangesAsync(); // Executes INSERT statement
}
Modifying Existing Entities
When you retrieve an entity from the database using EF Core, it's already being tracked by the DbContext
. If you make changes to its properties, EF Core automatically detects these changes when SaveChanges()
is called and generates an SQL UPDATE statement.
Example: Updating a Blog's URL
using (var db = new BloggingContext())
{
var blogToUpdate = await db.Blogs.FindAsync(1); // Retrieves and tracks the entity
if (blogToUpdate != null)
{
blogToUpdate.Url = "https://newexample.com/blog"; // Modify a property
await db.SaveChangesAsync(); // Executes UPDATE statement
}
}
Explicitly Attaching Modified Entities
If you have an entity that is not being tracked by the current DbContext
(e.g., it was created outside the context or the context was disposed and recreated), you can mark it as modified using Update()
or Attach()
followed by setting its state to Modified
.
var blogDisconnected = new Blog { BlogId = 1, Url = "https://disconnected.com/blog" };
db.Blogs.Update(blogDisconnected); // Or db.Attach(blogDisconnected); db.Entry(blogDisconnected).State = EntityState.Modified;
await db.SaveChangesAsync();
Update()
or marking an entity as Modified
for an untracked entity, EF Core will generate an UPDATE statement for all properties, even those that haven't changed. If you only want to update specific properties, use Entry(entity).Property(p => p.PropertyName).IsModified = true;
.
Deleting Entities
To delete an entity, you first retrieve it from the database (or have it available and attach it to the context) and then use the Remove()
method. EF Core tracks this entity as "Deleted". When SaveChanges()
is called, EF Core generates an SQL DELETE statement.
Example: Deleting a Blog
using (var db = new BloggingContext())
{
var blogToDelete = await db.Blogs.FindAsync(2);
if (blogToDelete != null)
{
db.Blogs.Remove(blogToDelete); // Mark the entity as Deleted
await db.SaveChangesAsync(); // Executes DELETE statement
}
}
Saving Changes for Multiple Operations
The DbContext
can track multiple entities undergoing different operations (add, update, delete) simultaneously. Calling SaveChanges()
will execute all necessary SQL statements in a single database round trip (often within a transaction).
Example: Adding, Updating, and Deleting
using (var db = new BloggingContext())
{
// Add a new blog
db.Blogs.Add(new Blog { Url = "https://newblog.com" });
// Update an existing blog
var existingBlog = await db.Blogs.FindAsync(1);
if (existingBlog != null)
{
existingBlog.Url = "https://updatedblog.com";
}
// Delete a blog
var blogToDelete = await db.Blogs.FindAsync(3);
if (blogToDelete != null)
{
db.Blogs.Remove(blogToDelete);
}
await db.SaveChangesAsync(); // Executes INSERT, UPDATE, and DELETE statements
}
Transaction Management
By default, SaveChanges()
wraps all generated SQL statements in a transaction. This ensures that either all operations succeed, or none of them do, maintaining data integrity. You can also explicitly manage transactions if you need more control.
Example: Explicit Transaction
using (var db = new BloggingContext())
{
using (var transaction = await db.Database.BeginTransactionAsync())
{
try
{
// Perform operations
db.Blogs.Add(new Blog { Url = "https://txblog.com" });
await db.SaveChangesAsync();
// Commit the transaction if all operations are successful
await transaction.CommitAsync();
}
catch
{
// Roll back the transaction if an error occurs
await transaction.RollbackAsync();
throw; // Re-throw the exception
}
}
}