Entity Framework (Ef) Code First Cascade Delete for One-To-Zero-Or-One Relationship

Entity Framework (EF) Code First Cascade Delete for One-to-Zero-or-One relationship

You will have to use the fluent API to do this.

Try adding the following to your DbContext:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasOptional(a => a.UserDetail)
.WithOptionalDependent()
.WillCascadeOnDelete(true);
}

EF Core Deleting related entities on One To One relationship

I see your One-to-One Fluent API configuration is not written in correct way as you did not specify the dependent entity. Your Fluent API configuration should be written as follows:

modelBuilder.Entity<Article>().HasOne(a => a.MediaPlan)
.WithOne(mp => mp.Article)
.HasForeignKey<Article>(a => a.MediaPlanId)
.OnDelete(DeleteBehavior.Cascade);

Now deleting a MediaPlan will also delete its dependent Article as follows:

var mediaPlanToBeDeleted = await _db.MediaPlans.FirstOrDefaultAsync(x=>x.Id == id);
_db.MediaPlans.Remove(mediaPlanToBeDeleted);
await _db.SaveChangesAsync();

Now if you want the reverse behavior then you have to reverse your Fluent API configuration.

Note: Only deleting the principal entity will cascade delete the dependent entity. Vice-versa is not possible.

Cascade delete in one to one relationship

You are right, it is your many-to-many relation ship between Article and Category: one Article has zero or more Categories and every Category may be used by zero or more Articles.

If you delete an Article, its Categories can't be deleted automatically, because the Category might be used by other Articles, and even if it isn't used right now, entity framework doesn't know whether you want to use it tomorrow. After all, you specified that every Category might be used by zero or more Articles.

Similarly, if you remove a Category, entity framework can't automatically remove the Articles belonging to this category.

This differs from a one-to-many relationship. For example, if you have a one-to-many relationship of a Book and its Pages, then every Book has zero or more Pages and every Page belongs to exactly one Book.

If you remove the Book, then entity framework knows that it should automatically remove all Pages of the Book, which are all Pages with a foreign key BookId. If Entity Framework would only remove the Book, then we would have a bunch of Pages with foreign key value pointing to a non-existing Book. So in one-to-many relations, entity framework can cascade on delete.

Alas, in many-to-many this is not possible.

On the bright side, you have the advantage that you can delete the last Article of a Category, and keep the Category intact. Tomorrow you can add a new Article that uses this Category.

So if you want to remove an article, you manually have to remove it from the 'Categories` it uses:

many-to-many following the standard naming conventions:

class Article
{
public int Id {get; set;}
// an Article belongs to zero or more Categories:
public virtual ICollection<Category> Categories {get; set;}
...
}
class Category
{
public int Id {get; set;}
// a Category is used by zero or more Articles:
public virtual ICollection<Article> Articles{get; set;}
...
}

Don't forget to declare your ICollections virtual!

class MyDbContext : DbContext
{
public class DbSet<Article> Articles {get; set;}
public class DbSet<Category> Categories {get; set;}
}

You don't have to mention the junction-table, entity framework will make it automatically for you, but you won't have to use it for joins if you want Articles with their Categories, or Categories with their Articles, just use the ICollections

Note: As Categories is not the expected plural of Category, you'll have to tell entity framework the proper table name. Out of scope of this question.

Delete an Article, but keep all Categories it belongs to alive:

using (var dbContext = new MyDbContext(...))
{
Article articleToRemove = ...
dbContext.Articles.Remove(articleToRemove);
dbContext.SaveChanges();
}

Entity framework will automatically perform the proper joins, and remove the articleToRemove from every Category. However, the Categories won't be removed.

In fact, internally the Categories table doesn't change at all. All records with Article.Id will be removed from the junction table.



Related Topics



Leave a reply



Submit