引入FOREIGN KEY约束可能会导致循环或多个级联路径.在删除时指定无操作

Min*_*ess 16 database sql-server asp.net-mvc entity-framework

用户,雇主,候选人和工作,雇主可以创建多个工作,每个工作只能有一个雇主,候选人可以申请许多工作,每个工作可以有多个申请成员.

所以这种关系是这样的:

在此输入图像描述

我正在使用实体框架代码的第一种方法,此时如果我删除一个雇主,它将从数据库中删除所有相关的工作和用户,如果我删除候选人,它将删除用户:

modelBuilder.Entity<Employer>()
     .HasRequired(e => e.User)
    .WithOptional(e => e.Employer).WillCascadeOnDelete();

//member is candidate
modelBuilder.Entity<Member>()
    .HasRequired(e => e.User)
    .WithOptional(e => e.Member).WillCascadeOnDelete();

modelBuilder.Entity<Employer>()
    .HasMany(a => a.Jobs)
    .WithRequired(b => b.Employer)
    .WillCascadeOnDelete();
Run Code Online (Sandbox Code Playgroud)

一切正常,除非我在候选人和作业之间指定多对多关系并使用"update-database"更新数据库,它给了我这个错误:

在表'MemberJobMap'上引入FOREIGN KEY约束'FK_dbo.MemberJobMap_dbo.Jobs_JobId'可能会导致循环或多个级联路径.指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束.无法创建约束.查看以前的错误.

这是我如何指定多对多的关系:

modelBuilder.Entity<Member>()
   .HasMany(m => m.Jobs)
   .WithMany(j => j.Members)
   .Map(c =>
   {
      c.MapLeftKey("Id");
      c.MapRightKey("JobId");
      c.ToTable("MemberJobMap");
   });
Run Code Online (Sandbox Code Playgroud)

当我添加迁移时:

CreateTable(
   "dbo.MemberJobMap",
   c => new
   {
      Id = c.String(nullable: false, maxLength: 128),
      JobId = c.Int(nullable: false),
   })
   .PrimaryKey(t => new { t.Id, t.JobId })
   .ForeignKey("dbo.Members", t => t.Id, cascadeDelete: true)
   .ForeignKey("dbo.Jobs", t => t.JobId, cascadeDelete: true)
   .Index(t => t.Id)
   .Index(t => t.JobId);   
Run Code Online (Sandbox Code Playgroud)

我尝试将cascadeDelete更改为false但是当我删除已应用作业的候选人或当我尝试删除具有应用候选人的作业时,这会给我错误.

如何解决这个错误?以便:

  1. 删除作业时,它将删除关联的candidatejobmap表行而不影响任何其他表
  2. 删除候选项时,它将删除关联的candidatejobmap表行和用户表行而不影响任何其他表
  3. 同时保持所有其他指定的级联删除操作相同

Min*_*ess 11

我已经解决了这个问题

出现此问题的原因是我有两个到CandidateJobMap表的级联删除路径:

如果我删除雇主,它将删除相关的雇主工作,这将删除CandidateJobMap表:

雇主 - > Jobs-> CandidateJobMap

如果我删除候选者,它将删除CandidateJobMap表:

成员 - > CandidateJobMap

因此,为了解决这个问题,我必须禁用其中一个删除路径,当您创建多对多关系时,不能指定WillCascadeDelete(false),因此您必须按如下方式更改迁移:

CreateTable(
   "dbo.MemberJobMap",
   c => new
   {
      Id = c.String(nullable: false, maxLength: 128),
      JobId = c.Int(nullable: false),
   })
   .PrimaryKey(t => new { t.Id, t.JobId })
   .ForeignKey("dbo.Members", t => t.Id, cascadeDelete: false) <--------cascade delete to false
   .ForeignKey("dbo.Jobs", t => t.JobId, cascadeDelete: true)
   .Index(t => t.Id)
   .Index(t => t.JobId);   
Run Code Online (Sandbox Code Playgroud)

现在因为你将cascade delete设置为false,当一个候选被删除时,它不会删除相关的CandidateJobMap行,当你尝试删除候选时它会导致另一个错误,它也是CandidateJobMap中的一个相关键,所以你有在删除候选者之前手动删除CandidateJobMap中的相关行:

//remove all applied jobs from user, without doing this, you will receive an error
foreach (var appliedjob in user.Member.Jobs.ToList())
{
    user.Member.Jobs.Remove(appliedjob);
}

//before you can delete the user
await UserManager.DeleteAsync(user);
Run Code Online (Sandbox Code Playgroud)

不确定这是不是最好的方式,但它对我有用.


Mik*_*ike 5

我意识到这已经很老了,但我遇到了同样的问题,有些评论没有得到解决。

特别是“您已经隐藏了问题...”

从多个路径进行级联应该起作用,因为删除任一祖先应该具有删除后代的作用,这是有效的。但这只是理论上的,实际上SQL Server不允许这样做,因此会出现错误。一种解决方案是在本文中解决此SQL Server多级联路径问题与触发

他建议删除所有有问题的级联操作,并将其全部替换为触发器以删除记录。我更喜欢在级联链的顶层工作。在他的示例中,我只是使用INSTEAD OF DELETE在孩子的“父母”记录中将其切断,然后让层叠来照顾其余的孩子。

我这样做有两个原因

  1. 应该在数据库中完成此操作,因为这是坏数据的最后一道防线……有点像在洗衣机洗衣服之前清空口袋。照顾那里的事情意味着您不必将代码复制到将来可能会基于该数据库构建的所有不同模型中,也不必依靠所有新手开发人员来照顾它。

  2. 在最高级的祖先执行此操作将保留所有其他关系,包括您将来可能添加的关系。

希望这会有所帮助,迈克