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();
Important: When using 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
        }
    }
}