Saving Data with Entity Framework Core

This section details how to save data to your database using Entity Framework Core (EF Core). Saving data involves adding, updating, and deleting entities and then persisting these changes to the database.

The DbContext and State Management

EF Core tracks the state of entities within its context. When you fetch an entity, EF Core attaches it to the DbContext and marks its state as Unchanged.

When you modify a tracked entity (e.g., change a property value), EF Core automatically detects the change and marks its state as Modified.

Adding New Entities

To add a new entity, create an instance of your entity class and add it to an appropriate DbSet<TEntity> property on your DbContext.

C# Example: Adding a Blog

using (var context = new BloggingContext())
{
    var newBlog = new Blog { Url = "http://example.com" };
    context.Blogs.Add(newBlog);
    context.SaveChanges();
}
                

When SaveChanges() is called, EF Core generates the necessary SQL commands to insert the new entity into the database.

Modifying Existing Entities

As mentioned, EF Core automatically tracks changes to entities that are already attached to the context. You can simply modify the properties of a tracked entity.

C# Example: Updating a Blog's URL

using (var context = new BloggingContext())
{
    var existingBlog = context.Blogs.FirstOrDefault(b => b.BlogId == 1);
    if (existingBlog != null)
    {
        existingBlog.Url = "http://newexample.com";
        context.SaveChanges();
    }
}
                

Alternatively, you can explicitly mark an entity as modified using context.Entry(entity).State = EntityState.Modified;. This is particularly useful when dealing with entities that are not currently tracked by the context.

C# Example: Updating an Unattached Entity

var blogToUpdate = new Blog { BlogId = 2, Url = "http://updatedurl.com" };
using (var context = new BloggingContext())
{
    context.Entry(blogToUpdate).State = EntityState.Modified;
    context.SaveChanges();
}
                
Note: When you set the state to Modified explicitly for an unattached entity, EF Core assumes all properties have been modified. If you only want to update specific properties, consider using context.Attach() and then marking only the changed properties.

Deleting Entities

To delete an entity, you first need to retrieve it and then mark it for deletion using the Remove() method on the context.

C# Example: Deleting a Blog

using (var context = new BloggingContext())
{
    var blogToDelete = context.Blogs.FirstOrDefault(b => b.BlogId == 3);
    if (blogToDelete != null)
    {
        context.Blogs.Remove(blogToDelete);
        context.SaveChanges();
    }
}
                

Similar to modifications, you can also delete an entity that is not currently tracked by the context by attaching it and setting its state to Deleted.

C# Example: Deleting an Unattached Entity

var blogToDelete = new Blog { BlogId = 4 };
using (var context = new BloggingContext())
{
    context.Entry(blogToDelete).State = EntityState.Deleted;
    context.SaveChanges();
}
                

Saving Multiple Changes

You can perform multiple add, update, and delete operations within a single DbContext instance and then call SaveChanges() once to persist all changes efficiently.

C# Example: Multiple Operations

using (var context = new BloggingContext())
{
    // Add a new blog
    context.Blogs.Add(new Blog { Url = "http://anotherblog.com" });

    // Update an existing blog
    var blogToUpdate = context.Blogs.Find(1);
    if (blogToUpdate != null)
    {
        blogToUpdate.Url = "http://updatedblogurl.com";
    }

    // Delete a blog
    var blogToDelete = context.Blogs.Find(2);
    if (blogToDelete != null)
    {
        context.Blogs.Remove(blogToDelete);
    }

    context.SaveChanges(); // All changes saved atomically
}
                
Tip: When you call SaveChanges(), EF Core attempts to save all pending changes in a single database transaction. If any part of the operation fails, the entire transaction is rolled back.

Concurrency Control

Concurrency conflicts can occur when multiple users or processes attempt to modify the same data simultaneously. EF Core provides mechanisms to handle these conflicts.

By default, EF Core uses a "last-in, first-out" strategy. If a conflict is detected during SaveChanges(), an DbUpdateConcurrencyException is thrown.

For more sophisticated concurrency handling (e.g., detecting which specific rows caused the conflict or implementing specific resolution strategies), you can configure concurrency tokens (e.g., using a timestamp column).

SaveChanges() Return Value

The SaveChanges() method returns an int representing the number of state entries that were successfully written to the database.