Cascade Delete with Entity Framework

asp.net-mvc asp.net-mvc-5 entity-framework

Question

I have these two models:

public partial class Country
{
        public Country()
        {
            this.Dinners = new HashSet<Dinner>();
        }

        public int CountryID { get; set; }
        public string Name { get; set; }
        public virtual ICollection<Dinner> Dinners { get; set; }
}

and

public partial class Dinner
{
        public Dinner()
        {
            this.TRsvps = new HashSet<TRsvp>();
        }

        public int DinnerID { get; set; }
        public System.DateTime EventDate { get; set; }
        public string Description { get; set; }
        public string HostedBy { get; set; }
        public Nullable<int> CountryID { get; set; }

        public virtual Country Country { get; set; }
}

I got a bit confused on how Entity Framework will act when a user tries to delete a parent entity (in our case it is the Country entity) that has child records (Dinners).

For example if I have the following code inside my mvc action method:-

public ActionResult DeleteConfirmed(int id)
{
    Country country = db.Countries.Find(id);
    db.Countries.Remove(country);
    db.SaveChanges();
    return RedirectToAction("Index");
}

And exception will be raised if I try to remove a country which has dinners, which sounds valid.

I tried modifying my code as follow, by including the Dinners when retrieving the Country object:

Country country = db.Countries.Include(a => a.Dinners).Single(a2 => a2.CountryId = id);
db.Countries.Remove(country);
db.SaveChanges();
return RedirectToAction("Index");

No exception will be raised, so I thought that EF would have deleted the child dinners, but what happens is that it updates the countryID FK inside the Dinners table to be null.... (Cascade Set to Null)

I tried looping over the Dinners collection as follows:

 public ActionResult DeleteConfirmed(int id)
 {
     Country country2 = db.Countries.Find(id) ;

     foreach(var d in country2.Dinners)
     {
         db.Dinners.Remove(d);
     }

     db.Countries.Remove(country2);
     db.SaveChanges();
     return RedirectToAction("Index");
}

but this raised the following error:

An exception of type 'System.InvalidOperationException' occurred in System.Core.dll but was not handled in user code

Additional information: Collection was modified; enumeration operation may not execute.

I realized that I should explicitly call the .Tolist() on the foreach to get the parent and all its children deleted as follows:

 foreach(var d in country2.Dinners.ToList())

Can anyone advice if I getting things wrong, or this is the only way to support cascade on delete using EF ?

Thanks

1
2
10/31/2014 2:41:51 PM

Popular Answer

You are describing the documented behaviour for cascade delete when the foreign key is nullable:

If a foreign key on the dependent entity is nullable, Code First does not set cascade delete on the relationship, and when the principal is deleted the foreign key will be set to null.

If you want it to cascade delete then the relationship is required and the foreign key should not be nullable. Change public Nullable<int> CountryID { get; set; } to public int CountryID { get; set; }

Reference:

http://msdn.microsoft.com/en-us/data/jj591620.aspx#CascadeDelete

Additional info following your comment You don't have to .Include to get cascade delete to work on required relationships i.e. once you have made the foreign key non-nullable. I am sure of this because that is how my application works.

I think you are observing that Remove marks your entire object graph for removal - whether required or not - in the same way that Add marks the entire graph for insertion. NB - I am not 100% sure of this bit so you should test this before you rely on it.

Further reading here:

using Dbset.Add Versus using EntityState.Added

Why Does Entity Framework Reinsert Existing Objects into My Database?

What is the difference between IDbSet.Add and DbEntityEntry.State = EntityState.Added?

1
5/23/2017 10:30:33 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow