Sep
06

Delete Dependents When Removing an Entity From Entity Framework

posted on 06 September 2011 in programming

Warning: Please consider that this post is over 12 years old and the content may no longer be relevant.

This post is based on Entity Framework 4.1 Code First Fluent API.
I have an Entity Framework model being used in the business layer called Patient, it has a collection of dependent entities called Responses, when a response needs to be deleted I simply remove it from the collection and expect that it’ll get deleted from the database. It doesn’t.

Response and Patient Model Diagram

// This doesn’t work as expected, it removes the relationship but not the Response entity. 
Patient.Responses.Remove(Patient.Responses.Where(r => r.Id == 1));

If I try to save this, I get the error:

System.InvalidOperationException: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.

What’s happening is the Response entity does not get deleted, just the relationship to the Patient. However when the relationship is removed, Entity Framework doesn’t know what to do with Response.PatientId, it can’t null it because it’s marked as not null.

Reading this blog post helped me understand the issue a bit more. So one solution is to make the Response entity primary key depend on the foreign key field, i.e. PatientId, this will make Entity Framework act as expected and delete the dependent entity when the relationship is deleted.

We can do this by providing the following Fluent API mapping:

modelBuilder.Entity().HasKey(m => new { m.Id, m.PatientId }); 

But if you’re not comfortable changing your database structure to suit Entity Framework there is another way. By overriding the SaveChanges method of our DbContext class, we can monitor for changes to Responses where they have been orphaned from a Patient and delete them:

public override int SaveChanges()
{
  // Need to manually delete all responses that have been removed from the patient, otherwise they'll be orphaned.
  var orphanedResponses = ChangeTracker.Entries().Where(
    e => (e.State == EntityState.Modified || e.State == EntityState.Added) &&
      e.Entity is Response &&
        e.Reference("Patient").CurrentValue == null);
  foreach (var orphanedResponse in orphanedResponses)
  {
    Responses.Remove(orphanedResponse.Entity as Response);
  }

  return base.SaveChanges();
}