Deleting related entities with entity framework

Entity framework has some features and peculiarities that are useful in a lot of situations and with they can help us to do some operations in very smart and efficient mode.

Anyway in some circumstances it could surprise us with some very strange side effects or behaviors that may we don’t know; we can meet one of these in the deletion of an entity and the related entities.

Deletion

Suppose that we have a Post table, with the related entities Categories and Tags; of course the last two have a foreign key to the Post table:

[Table("Post")]
public class Post
{
public Guid Id { get; set; }
public string Title { get; set; }
public virtual ICollection<Category> Categories { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}

[Table("Category")]
public class Category
{
public Guid Id { get; set; }
public string Description { get; set; }
public Guid PostId { get; set; }
[ForeignKey("PostId")]
public virtual Post Post { get; set; }
}

[Table("Tag")]
public class Tag
{
public Guid Id { get; set; }
public string Description { get; set; }
public Guid PostId { get; set; }
[ForeignKey("PostId")]
public virtual Post Post { get; set; }
}

Now if we want to delete the Post entity and the related we can do this:

using (var db = new Context())
{
var categories = db.Categories.Where(c => c.PostId == postEntity.Id);
var tags = db.Tags.Where(c => c.PostId == postEntity.Id);
var post = await db.Posts.FirstAsync(p => p.Id == postEntity.Id);

db.Categories.RemoveRange(categories);
db.Tags.RemoveRange(tags);
db.Posts.Remove(post);

await db.SaveChangesAsync().ConfigureAwait(false);
}

This is simple and a commonly used pratice, but we have an alternative.

We can delete some rows and load all the related entities with the include method:

using (var db = new Context())
{
var post = await db.Posts.Include(p => p.Categories).Include(p => p.Tags).FirstOrDefaultAsync(p => p.Id == postEntity.Id);

db.Categories.RemoveRange(post.Categories);
db.Tags.RemoveRange(post.Tags);
db.Posts.Remove(post);

await db.SaveChangesAsync().ConfigureAwait(false);
}

Better, we have reduced the code and improved the performance by loading the entity and the relations with one shot.

Anyway what happens if we forget the deletion of one of the related entities?

PrepareToSaveChanges method

As we have seen before, we have at least two different methods to remove the related entities; with the first one, we write some code like this:

var categories = db.Categories.Where(c => c.PostId == postEntity.Id); 
var post = await db.Posts.FirstAsync(p => p.Id == postEntity.Id); 
db.Categories.RemoveRange(categories);
db.Posts.Remove(post); 
await db.SaveChangesAsync().ConfigureAwait(false);

We have forget the deletion of the tags and we will get an DbUpdateException, like this:

System.Data.Entity.Infrastructure.DbUpdateException : An error occurred while updating the entries. See the inner exception for details.
----> System.Data.Entity.Core.UpdateException : An error occurred while updating the entries. See the inner exception for details.
----> System.Data.SqlClient.SqlException : The DELETE statement conflicted with the REFERENCE...

But if we use the second approach and write this code:

using (var db = new Context())
{
var post = await db.Posts.Include(p => p.Categories).Include(p => p.Tags).FirstOrDefaultAsync(p => p.Id == postEntity.Id);

db.Categories.RemoveRange(post.Categories);
db.Posts.Remove(post);

await db.SaveChangesAsync().ConfigureAwait(false);
}

Entity framework will fail with this error:

Message: 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.

We will have an InvalidOperationException, thrown by the PrepareToSaveChanges method of the ObjectContext.

This is a method called when we execute the SaveChanges that among other things check some preconditions; in detail, because we have included the tag related entity but we haven’t removed that, this entity is attached to the context and so entity framework check if the foreign key of the tag entity is nullable.

This is an example of a particular behaviour of entity framework.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s