How to access navigation property of deleted entity

By Mirek on (tags: Code First, Entity Framework, navigation property, categories: code)

In this post I am going to show you a small Entity Framework code first trick. When you remove an entity from the context its state is set to Deleted and all its navigation properties are nulled. This is done by DbContext internally. However you might want to know what was the related entities (navigation properties) before the entity was deleted. For instance you want to create some audit logs of deleted entity. I will show you how to do it with minimal effort.

Let’s say we have a Customer and Order entities in our domain.

public class Customer
{
    public int Id { get; set; }
 
    public string Name { get; set; }
 
    public ICollection<Order> Orders { get; set; }
}
 
public class Order
{
    public int Id { get; set; }
 
    public Customer Customer { get; set; }
 
    public int Quantity { get; set; }
}

As you can see the Order entity has a navigation property to Customer. Now let’s say we have some customers and associated order in the database and we want to remove an order with id=1. Additionally, after removing it from the context but before deleting it from the database we want to get know which Customer the deleted Order was assigned to.

using (var ctx = new AppContext())
{
    var order = ctx.Orders.Find(1);
    ctx.Orders.Remove(order);
    var customer = order.Customer; //NULL!
    ctx.SaveChanges();
}

Unfortunately the Customer property here is null and we have to find another way to retrieve the required information.
First let’s introduce new property in class Order.

public class Order
{
    public int Id { get; set; }
 
    public Customer Customer { get; set; }
 
    public int CustomerId { get; set; }
 
    public int Quantity { get; set; }
}

The CustomerId property by default will be correlated with Customer navigation property. This is done by Entity Framework and is a consequence of default naming convention. In another words the CustomerId property will always hold the id of the related Customer entity. Now having that in place we can use a change tracker entries to retrieve this property from deleted order.

   1: using (var ctx = new AppContext())
   2: {
   3:     var order = ctx.Orders.Find(1);
   4:     ctx.Orders.Remove(order);
   5:  
   6:     var orderentry = ctx.ChangeTracker.Entries<Order>()
   7:         .Where(x => x.State == EntityState.Deleted).FirstOrDefault();
   8:     if (orderentry != null)
   9:     {
  10:         var customerId = orderentry.Property(o => o.CustomerId).OriginalValue;
  11:         var customer = ctx.Customers.Find(customerId);
  12:     }
  13:  
  14:     ctx.SaveChanges();
  15: }
In line number 6 the deleted order entry is queried then in line 10 the original value of the CustomerId propety is retrieved. Now having the id of the customer we can easily get the Customer object by calling Find on the context (line 11). Since customer is already tracked by EF it will be returned immediately.