MSDN Documentation

.NET Concepts: Data Access

Entity Framework Core: Saving Data

Saving data in Entity Framework Core (EF Core) is a fundamental operation. EF Core provides a streamlined and efficient way to persist changes made to your entity objects to the database. This involves tracking changes and then calling the SaveChanges or SaveChangesAsync method on your DbContext instance.

Understanding Change Tracking

EF Core's change tracker is the core component responsible for detecting modifications to your entities. When you retrieve an entity from the database using EF Core, the context tracks its original state. Any subsequent modifications made to the entity's properties are then compared against this original state.

Adding New Entities

To add a new entity to your database, you first create an instance of your entity class and then attach it to the DbContext using the Add or AddAsync method.


using (var context = new YourDbContext())
{
    var newProduct = new Product
    {
        Name = "New Gadget",
        Price = 99.99m
    };

    context.Products.Add(newProduct);
    await context.SaveChangesAsync(); // Or SaveChanges() for synchronous
}
                    

Modifying Existing Entities

When you retrieve an entity and modify its properties, EF Core's change tracker automatically detects these changes. You don't need to explicitly call an "update" method for entities that are already being tracked.


using (var context = new YourDbContext())
{
    var productToUpdate = await context.Products.FindAsync(1); // Assuming product with ID 1 exists

    if (productToUpdate != null)
    {
        productToUpdate.Price = 109.99m; // EF Core will detect this change
        await context.SaveChangesAsync();
    }
}
                    

Important: If you detach an entity from the context and then modify it, you need to explicitly tell EF Core about the modifications. You can do this using context.Entry(entity).State = EntityState.Modified;.

Deleting Entities

To delete an entity, you first retrieve it and then use the Remove or RemoveRange method on the DbSet.


using (var context = new YourDbContext())
{
    var productToDelete = await context.Products.FindAsync(2); // Assuming product with ID 2 exists

    if (productToDelete != null)
    {
        context.Products.Remove(productToDelete);
        await context.SaveChangesAsync();
    }
}
                    

SaveChanges() vs. SaveChangesAsync()

SaveChanges() is the synchronous method for persisting changes, while SaveChangesAsync() is the asynchronous counterpart. For I/O-bound operations like database interactions, using the asynchronous version is highly recommended to keep your application responsive, especially in web applications.

Saving Multiple Changes

You can perform multiple add, modify, and delete operations within a single unit of work before calling SaveChanges() or SaveChangesAsync(). EF Core will generate a single, batched SQL statement (where possible) to apply all these changes efficiently.


using (var context = new YourDbContext())
{
    // Add a new product
    var newProduct = new Product { Name = "Smart Watch", Price = 199.99m };
    context.Products.Add(newProduct);

    // Update an existing product
    var existingProduct = await context.Products.FindAsync(3);
    if (existingProduct != null)
    {
        existingProduct.Price = 219.99m;
    }

    // Delete a product
    var productToRemove = await context.Products.FindAsync(4);
    if (productToRemove != null)
    {
        context.Products.Remove(productToRemove);
    }

    // Save all changes at once
    int affectedRows = await context.SaveChangesAsync();
    Console.WriteLine($"{affectedRows} rows affected.");
}
                    

DbContext.Entry() for Fine-Grained Control

The DbContext.Entry() method provides access to the change tracker's metadata for a specific entity. This allows for more advanced scenarios, such as manually setting the state of an entity or controlling which properties are updated.

Example: Manually Setting Entity State


// If you have an entity that is not tracked or you want to re-attach it
var detachedProduct = new Product { Id = 5, Name = "Old Phone", Price = 500.00m };

context.Entry(detachedProduct).State = EntityState.Modified;
await context.SaveChangesAsync();
                    

Example: Property-Specific Updates


var product = await context.Products.FindAsync(6);
if (product != null)
{
    // Only update the Price property
    context.Entry(product).Property(p => p.Price).IsModified = true;
    // Note: EF Core will still detect other property changes if not explicitly disabled.
    // This is useful when you want to ensure only specific properties are updated
    // or when dealing with concurrency.
    product.Name = "Updated Phone Name"; // This will NOT be saved if only Price is marked modified without further action.

    await context.SaveChangesAsync();
}
                    

Understanding EF Core's change tracking and the nuances of SaveChanges() is crucial for efficient and correct data persistence. Always consider using asynchronous operations for database calls in production environments.