Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specifying ON DELETE NO ACTION in Entity Framework 7? [duplicate]

In Entity Framework 7 when I am trying to apply a migration I get the error

Introducing FOREIGN KEY constraint 'FK_ChangeOrder_User_CreatedByID' on table 'ChangeOrder' 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 in older versions of Entity Framework you would deal with this by adding

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); 

to the DbContext but in EF7 modelBuilder does not seem to have a .Conventions to it and google is only returning older EF 4 though EF 6 results.

How do I specific the ON DELETE NO ACTION constraint in Entity Framework 7?

Edit: The answer provided by Oleg will apparently do it per Foreign Key but I would like to do it globally as it will much easier to use one line of code to declare this globally then have to specify code it out for every single one of the hundreds of relationships I will end up having.

Edit 2: Code for Oleg

public class ChangeOrder {     [DatabaseGenerated(DatabaseGeneratedOption.Identity)]     public int ID { get; set; }      public Int16? ApprovedByID { get; set; }     public Byte ApprovalStatusID { get; set; }     public Int16 AssignedToID { get; set; }     public Int16 CreatedByID { get; set; }     public Byte CurrentStatusID { get; set; }     public DateTime? DateApproved { get; set; }     public DateTime? EndDate { get; set; }     public Byte ImpactID { get; set; }     public Byte PriorityID { get; set; }     public DateTime? StartDate { get; set; }     public Byte TypeID { get; set; }      [Required]     public string Name { get; set; }      [Required]     public string ReasonForChange { get; set; }      [ForeignKey("ApprovedByID")]     public User ApprovedBy { get; set; }      [ForeignKey("ApprovalStatusID")]     public ChangeApprovalStatus ApprovalStatus { get; set; }      [ForeignKey("AssignedToID")]     public User AssignedTo { get; set; }      [ForeignKey("CreatedByID")]     public User CreatedBy { get; set; }      [ForeignKey("ImpactID")]     public ChangeImpact Impact { get; set; }      [ForeignKey("PriorityID")]     public ChangePriority Priority { get; set; }      [ForeignKey("TypeID")]     public ChangeType ChangeType { get; set; }      [ForeignKey("CurrentStatusID")]     public ChangeStatus CurrentStatus { get; set; } } 
public class JobSightDBContext : DbContext {     protected override void OnModelCreating(ModelBuilder modelbuilder)     {         base.OnModelCreating(modelbuilder);     }      DbSet<ChangeApprovalStatus> ChangeApprovalStatus { get; set; }     DbSet<ChangeImpact> ChangeImapct { get; set; }     DbSet<ChangeOrder> ChangeOrders { get; set; }     DbSet<ChangePriority> ChangePriorities { get; set; }     DbSet<ChangeStatus> ChangeStatus { get; set; }     DbSet<ChangeType> ChangeTypes { get; set; }     DbSet<User> Users { get; set; } } 
like image 534
Matthew Verstraete Avatar asked Jan 13 '16 14:01

Matthew Verstraete


People also ask

Does Entity Framework cascade DELETE?

The EF Core in-memory database does not currently support cascade deletes in the database.

How to cascade DELETE Entity Framework Core?

Cascade delete always works in one direction - from principal entity to dependent entity, i.e. deleting the principal entity deletes the dependent entities. And for one-to- many relationships the one side is always the principal and the many side is the dependent. Entity<B>(). HasMany(b => b.As).

What is on Delete no action?

ON DELETE NO ACTION : SQL Server raises an error and rolls back the delete action on the row in the parent table. ON DELETE CASCADE : SQL Server deletes the rows in the child table that is corresponding to the row deleted from the parent table.


2 Answers

After digging around on GitHub, and working with a very patient guy from MS there, the current solution is to add this to the DbContext

protected override void OnModelCreating(ModelBuilder modelbuilder) {     foreach (var relationship in modelbuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))     {         relationship.DeleteBehavior = DeleteBehavior.Restrict;     }      base.OnModelCreating(modelbuilder); } 
like image 162
Matthew Verstraete Avatar answered Sep 23 '22 23:09

Matthew Verstraete


The construction

modelBuilder.Entity("myNamespace.Models.ChangeOrder", b =>     {         b.HasOne("myNamespace.Models.User")             .WithMany()             .HasForeignKey("CreatedByID")             .OnDelete(DeleteBehavior.Cascade);     }); 

will means creating FK_ChangeOrder_User_CreatedByID with REFERENCES [dbo].[User] ([CreatedByID]) ON DELETE CASCADE. It should exist in protected override void BuildModel(ModelBuilder modelBuilder) of YourContextModelSnapshot.cs created during migration. I'm not sure that I full understand your question, but I think that you should either add such construct to XXXModelSnapshot.cs or to remove unneeded construct, which already exist here.

UPDATED: I see that you have the problem in the Model. You have the following properties in

public Int16? ApprovedByID { get; set; } public Int16 AssignedToID { get; set; } public Int16 CreatedByID { get; set; }  // navigation properties  [ForeignKey("ApprovedByID")] public User ApprovedBy { get; set; }  [ForeignKey("AssignedToID")] public User AssignedTo { get; set; }  [ForeignKey("CreatedByID")] public User CreatedBy { get; set; } 

By default migration try to set DeleteBehavior.Cascade on all the properties.

You can overwrite the behavior by changing OnModelCreating, which sets either DeleteBehavior.Restrict behavior for all the keys or to set on one only key the DeleteBehavior.Cascade or DeleteBehavior.SetNull behavior. For example, the below code uses DeleteBehavior.Cascade on CreatedByID (which creates ON DELETE CASCADE on the foreign keys) and DeleteBehavior.Restrict on other foreign keys (no ON DELETE on the foreign keys):

public class JobSightDBContext : DbContext {     protected override void OnModelCreating(ModelBuilder modelbuilder)     {         base.OnModelCreating(modelbuilder);          modelbuilder.Entity(typeof (ChangeOrder))             .HasOne(typeof (User), "ApprovedBy")             .WithMany()             .HasForeignKey("ApprovedByID")             .OnDelete(DeleteBehavior.Restrict); // no ON DELETE         modelbuilder.Entity(typeof (ChangeOrder))             .HasOne(typeof (User), "AssignedTo")             .WithMany()             .HasForeignKey("AssignedToID")             .OnDelete(DeleteBehavior.Restrict); // no ON DELETE         modelbuilder.Entity(typeof (ChangeOrder))             .HasOne(typeof (User), "CreatedBy")             .WithMany()             .HasForeignKey("CreatedByID")             .OnDelete(DeleteBehavior.Cascade); // set ON DELETE CASCADE     }      DbSet<ChangeApprovalStatus> ChangeApprovalStatus { get; set; }     DbSet<ChangeImpact> ChangeImapct { get; set; }     DbSet<ChangeOrder> ChangeOrders { get; set; }     DbSet<ChangePriority> ChangePriorities { get; set; }     DbSet<ChangeStatus> ChangeStatus { get; set; }     DbSet<ChangeType> ChangeTypes { get; set; }     DbSet<User> Users { get; set; } } 
like image 35
Oleg Avatar answered Sep 23 '22 23:09

Oleg