Entity Framework Code First 5 Cascade Delete on ma

2020-02-26 10:07发布

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?

1条回答
仙女界的扛把子
2楼-- · 2020-02-26 10:16

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);
}
查看更多
登录 后发表回答