Managing Entities with Entity Framework

Entity Framework (EF) provides powerful mechanisms for managing entities, which represent the objects in your application domain and are mapped to rows in your database tables. This section delves into the core concepts and operations involved in working with entities.

Understanding Entity States

Each entity tracked by Entity Framework has a specific state that indicates how it relates to the data source. The primary states are:

The DbContext and Change Tracking

The DbContext is the primary class for interacting with Entity Framework. It acts as a gateway to your data and is responsible for tracking changes to entities.

Change Tracking Example

When you query an entity using a DbContext instance, EF automatically starts tracking its state. Any modifications you make to the entity's properties will be recorded.


using (var context = new MyDbContext())
{
    var product = context.Products.Find(1); // State: Unchanged

    if (product != null)
    {
        product.Name = "Updated Product Name"; // State: Modified
        context.SaveChanges(); // Changes are persisted to the database
    }
}
                

Adding New Entities

To add a new entity to the database, you create an instance of your entity class and add it to a tracked DbSet within your DbContext. EF will then mark the entity as Added.


var newProduct = new Product { Name = "New Gadget", Price = 99.99m };
context.Products.Add(newProduct); // State: Added
context.SaveChanges(); // Inserts the new product into the database
            

Modifying Existing Entities

As demonstrated in the change tracking example, modifying an entity that is already being tracked by the DbContext is straightforward. EF automatically detects property changes and marks the entity as Modified when SaveChanges is called.

Tip: For detached entities, you can explicitly mark them as modified using context.Entry(entity).State = EntityState.Modified; if you know the entire entity has been changed.

Deleting Entities

To delete an entity, you can either retrieve it and call the Remove method on the DbSet, or if you have the entity's key, you can create a "ghost" entity (an entity with only its primary key populated) and pass it to Remove.

Deleting a Retrieved Entity


var productToDelete = context.Products.Find(5);
if (productToDelete != null)
{
    context.Products.Remove(productToDelete); // State: Deleted
    context.SaveChanges(); // Deletes the product from the database
}
            

Deleting with a Key (Ghost Entity)


var productToDelete = new Product { ProductId = 10 }; // Only Id is needed
context.Products.Remove(productToDelete); // State: Deleted
context.SaveChanges(); // Deletes the product from the database
            

Handling Detached Entities

Entities can become detached from the DbContext when the context is disposed, or if they are created outside of the context's tracking. To reattach and update a detached entity:

  1. Retrieve the entity from the database using the same DbContext instance.
  2. Update the properties of the retrieved entity with the values from the detached entity.
  3. Call SaveChanges.

Alternatively, you can attach the detached entity and set its state:


var detachedProduct = new Product { ProductId = 15, Name = "Updated Name" };
context.Attach(detachedProduct);
context.Entry(detachedProduct).State = EntityState.Modified;
context.SaveChanges();
            

Warning: When attaching a detached entity and setting its state to Modified without explicitly loading it first, EF assumes all properties have been modified. This can lead to unintended updates. It's often safer to load the entity first or specify which properties have changed.

Concurrency Control

Entity Framework supports concurrency control to manage situations where multiple users or processes might try to update the same data simultaneously. EF uses optimistic concurrency, where conflicts are detected at the time SaveChanges is called.

You can configure concurrency checks by marking properties (e.g., a timestamp or version column) with the [ConcurrencyCheck] attribute or by configuring it in the Fluent API. When a concurrency conflict occurs, EF will throw a DbUpdateConcurrencyException.

Note: Proper handling of DbUpdateConcurrencyException is crucial for robust applications. You may need to re-query data, resolve conflicts, and retry the save operation.

Further Reading