Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework Code First 5 Cascade Delete on many to many tables error

I've this model

public class State
{
    public State()
    {
        this.Promotions = new List<Promotion>();
        this.Branches = new List<Branch>();
        this.Stores = new List<Store>();
    }

    public int Id { get; set; }
    public string Description { get; set; }

    public virtual ICollection<Promotion> Promotions { get; set; }
    public virtual ICollection<Store> Stores { get; set; }
    public virtual ICollection<Branch> Branches { get; set; }
}

public class Store
{
    public Store()
    {
        this.Promotions = new List<Promotion>();
        this.Branches = new List<Branch>();
    }

    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Promotion> Promotions { get; set; }
    public virtual ICollection<Branch> Branches { get; set; }

    public int StateId { get; set; } // Foreign key
    public virtual State State { get; set; } // Navigation Property
}

public class Branch
{
    public Branch()
    {
        this.Promotions = new List<Promotion>();
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public int StoreId { get; set; } // Foreign key
    public int StateId { get; set; } // Foreign key

    public virtual Store Store { get; set; } // Navigation Property
    public virtual State State { get; set; } // Navigation Property

    public virtual ICollection<Promotion> Promotions { get; set; }
}

    public class Promotion
{
    public Promotion()
    {
        this.Stores = new List<Store>();
        this.Branches = new List<Branch>();
        this.Productos = new List<Producto>();
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public int StateId { get; set; }

    public virtual ICollection<Store> Stores { get; set; }
    public virtual ICollection<Branch> Branches { get; set; }
    public virtual ICollection<Product> Products { get; set; }

    public virtual State State { get; set; }

}

And this in my context:

// State
modelBuilder.Entity<State>()
    .HasMany(p => p.Promotions)
    .WithRequired(e => e.State)
    .WillCascadeOnDelete(false);

modelBuilder.Entity<State>()
    .HasMany(s => s.Branches)
    .WithRequired(e => e.State)
    .WillCascadeOnDelete(false);

modelBuilder.Entity<State>()
   .HasMany(e => e.Stores)
   .WithRequired(e => e.State)
   .WillCascadeOnDelete(true);

 // Store
modelBuilder.Entity<Store>()
    .HasMany(b => b.Branches)
    .WithRequired(s => s.Store)
    .WillCascadeOnDelete(true);

// Many to many
modelBuilder.Entity<Store>().
  HasMany(p => p.Promotions).
  WithMany(s => s.Stores).
  Map(
   m =>
   {
       m.MapLeftKey("StoreId");
       m.MapRightKey("PromotionId");
       m.ToTable("Store_Promotion");
   });

modelBuilder.Entity<Promotion>().
 HasMany(e => e.Products).
 WithMany(p => p.Promotiones).
 Map(
  m =>
  {
      m.MapLeftKey("PromotionId");
      m.MapRightKey("ProductoId");
      m.ToTable("Promotion_Producto");
  });

modelBuilder.Entity<Branch>().
 HasMany(p => p.Promotiones).
 WithMany(b => b.Branches).
 Map(
  m =>
  {
      m.MapLeftKey("BranchId");
      m.MapRightKey("PromotionId");
      m.ToTable("Branch_Promotion");
  });

Now if I turn on more than one WillCascadeOnDelete of the State (first three in the fluent mapping) I get the error

Test method Proj.Data.Tests.UnitTest1.TestPromotion threw exception:
System.Data.SqlClient.SqlException: Introducing FOREIGN KEY constraint 'FK_dbo.Branch_dbo.Store_StoreId' on table 'Branch' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. Could not create constraint. See previous errors.

I know that, and I've read from Julie Lerman's book:

Some databases (including SQL Server) don’t support multiple relationships that specify cascade delete pointing to the same table

As it happens since the many to many relationship table has cascade delete coming from both related tables.

So, my question is: The only choice here is to turn off cascade delete on parent tables and handle the deletion on relationship table manually? Isn't there any workaround from Entity Framework 5 for this?

like image 925
polonskyg Avatar asked Nov 15 '12 15:11

polonskyg


1 Answers

Ok, I understood the problem. It is not to have a many to many relationship, the problem is this

State -> Promotion -> PromotionStore
State -> Branch -> BranchPromotion
State -> Store -> StorePromotion

and then Store, Branch and Store have FK to State. So if I delete a State PromotionStore can be reached by 1st and 3rd possibilities.

What I ended up doing is turning off cascade delete for State and deleting the related records manually like this:

public override void Delete(State state)
{
   DbContext.Entry(state).Collection(x => x.Promotions).Load();
   DbContext.Entry(state).Collection(x => x.Stores).Load();
   DbContext.Entry(state).Collection(x => x.Branches).Load();

   var associatedPromotions = state.Promotions.Where(p => p.StateId == state.Id);
   associatedPromotions.ToList().ForEach(r => DbContext.Set<Promotion>().Remove(r));

   var associatedStores = state.Stores.Where(e => e.StateId == state.Id);
   associatedStores.ToList().ForEach(e => DbContext.Set<Store>().Remove(e));

   var associatedBranches = state.Branches.Where(s => s.StateId == state.Id);
   associatedBranches.ToList().ForEach(s => DbContext.Set<Branch>().Remove(s));

   base.Delete(state);
}
like image 135
polonskyg Avatar answered Oct 26 '22 05:10

polonskyg