引入FOREIGN KEY约束可能会导致循环或多个级联路径 - 为什么?

时间:2013-06-15 19:52:56

标签: .net entity-framework entity-framework-4 ef-code-first

我一直在努力解决这个问题,并且无法弄清楚发生了什么。我有一个卡片实体,其中包含Sides(通常为2个) - 卡片和侧面都有一个舞台。我正在使用EF Codefirst迁移,并且迁移失败并出现此错误:

  

介绍FOREIGN KEY约束'FK_dbo.Sides_dbo.Cards_CardId'on   table'Sides'可能会导致循环或多个级联路径。指定ON   DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY   约束

这是我的实体:

public class Card
{
    public Card()
    {
        Sides = new Collection<Side>();
        Stage = Stage.ONE;
    }

    [Key]
    [Required]
    public virtual int CardId { get; set; }

    [Required]
    public virtual Stage Stage { get; set; }

    [Required]
    [ForeignKey("CardId")]
    public virtual ICollection<Side> Sides { get; set; }
}

这是我的 Side 实体:

public class Side
{
    public Side()
    {
        Stage = Stage.ONE;
    }

    [Key]
    [Required]     
    public virtual int SideId { get; set; } 

    [Required]
    public virtual Stage Stage { get; set; }

    [Required]
    public int CardId { get; set; }

    [ForeignKey("CardId")]
    public virtual Card Card { get; set; }

}

这是我的舞台实体:

public class Stage
{
    // Zero
    public static readonly Stage ONE = new Stage(new TimeSpan(0, 0, 0), "ONE");
    // Ten seconds
    public static readonly Stage TWO = new Stage(new TimeSpan(0, 0, 10), "TWO");

    public static IEnumerable<Stage> Values
    {
        get
        {
            yield return ONE;
            yield return TWO;
        }

    }

    public int StageId { get; set; }
    private readonly TimeSpan span;
    public string Title { get; set; }

    Stage(TimeSpan span, string title)
    {
        this.span = span;
        this.Title = title;
    }

    public TimeSpan Span { get { return span; } }
}

奇怪的是,如果我将以下内容添加到我的Stage类中:

    public int? SideId { get; set; }
    [ForeignKey("SideId")]
    public virtual Side Side { get; set; }

迁移成功运行。如果我打开SSMS并查看表格,我可以看到Stage_StageId已添加到Cards(按预期/期望),但Sides不包含Stage的引用(未预期)。

如果我再添加

    [Required]
    [ForeignKey("StageId")]
    public virtual Stage Stage { get; set; }
    public int StageId { get; set; }

在我的Side课程中,我看到StageId列已添加到我的Side表格中。

这是有效的,但现在在我的应用程序中,对Stage的任何引用都包含SideId,在某些情况下完全无关紧要。 我想基于上述Stage类为我的CardSide实体提供Stage属性,而不会在可能的情况下使用引用属性污染舞台类 ... 我究竟做错了什么?

18 个答案:

答案 0 :(得分:334)

由于Stage 必需,所以涉及Stage的所有一对多关系默认情况下会启用级联删除。这意味着,如果您删除Stage实体

  • 删除将直接级联到Side
  • 删除将直接级联到Card,因为CardSide具有必需的一对多关系,默认情况下启用了级联删除,然后它会从{{{1}级联1}}到Card

因此,您有两个从SideStage的级联删除路径 - 这会导致异常。

您必须在至少一个实体中设置Side可选项(即从Stage属性中删除[Required]属性)或使用Fluent API禁用级联删除(不可能)使用数据注释):

Stage

答案 1 :(得分:44)

我有一张与其他人有圆形关系的桌子,我得到同样的错误。原来它是关于不可空的外键。如果key不可为空,则必须删除相关对象,并且循环关系不允许这样做。所以使用可以为空的外键。

[ForeignKey("StageId")]
public virtual Stage Stage { get; set; }
public int? StageId { get; set; }

答案 2 :(得分:25)

有人想知道如何在EF核心中做到这一点:

{{1}}

答案 3 :(得分:20)

当我从EF7模型迁移到EF6版本时,我收到了很多实体的错误。我不想一次一个地浏览每个实体,所以我使用了:

builder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
builder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

答案 4 :(得分:16)

您可以将cascadeDelete设置为false或true(在您的迁移Up()方法中)。取决于您的要求。

AddForeignKey("dbo.Stories", "StatusId", "dbo.Status", "StatusID", cascadeDelete: false);

答案 5 :(得分:5)

我也有这个问题,我用this answer from a similar thread

立即解决了这个问题

在我的情况下,我不想删除密钥删除的依赖记录。如果在您的情况下就是这种情况,只需将迁移中的布尔值更改为false:

AddForeignKey("dbo.Stories", "StatusId", "dbo.Status", "StatusID", cascadeDelete: false);

如果你正在创建抛出这个编译器错误但是想要保持级联删除的关系,那么很可能;你的人际关系存在问题。

答案 6 :(得分:5)

在.NET Core中,我将onDelete选项更改为ReferencialAction.NoAction

         constraints: table =>
            {
                table.PrimaryKey("PK_Schedule", x => x.Id);
                table.ForeignKey(
                    name: "FK_Schedule_Teams_HomeId",
                    column: x => x.HomeId,
                    principalTable: "Teams",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.NoAction);
                table.ForeignKey(
                    name: "FK_Schedule_Teams_VisitorId",
                    column: x => x.VisitorId,
                    principalTable: "Teams",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.NoAction);
            });

答案 7 :(得分:5)

我修好了。添加迁移时,在Up()方法中会出现如下所示的行:

<div class="js-player"></div>
<div class="js-player"></div>
<div class="js-player"></div>

<div class="filler"></div>

<div id="mute">mute</div>

如果您只是从结尾删除cascadeDelete,它将起作用。

答案 8 :(得分:3)

仅出于文档目的,对于未来的人来说,这件事可以像这样简单解决,并且使用这种方法,你可以做一个禁用一次的方法,你可以正常访问你的方法

将此方法添加到上下文数据库类:

protected override void OnModelCreating(DbModelBuilder modelBuilder) {
    modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}

答案 9 :(得分:2)

public partial class recommended_books : DbMigration
{
    public override void Up()
    {
        CreateTable(
            "dbo.RecommendedBook",
            c => new
                {
                    RecommendedBookID = c.Int(nullable: false, identity: true),
                    CourseID = c.Int(nullable: false),
                    DepartmentID = c.Int(nullable: false),
                    Title = c.String(),
                    Author = c.String(),
                    PublicationDate = c.DateTime(nullable: false),
                })
            .PrimaryKey(t => t.RecommendedBookID)
            .ForeignKey("dbo.Course", t => t.CourseID, cascadeDelete: false) // was true on migration
            .ForeignKey("dbo.Department", t => t.DepartmentID, cascadeDelete: false) // was true on migration
            .Index(t => t.CourseID)
            .Index(t => t.DepartmentID);

    }

    public override void Down()
    {
        DropForeignKey("dbo.RecommendedBook", "DepartmentID", "dbo.Department");
        DropForeignKey("dbo.RecommendedBook", "CourseID", "dbo.Course");
        DropIndex("dbo.RecommendedBook", new[] { "DepartmentID" });
        DropIndex("dbo.RecommendedBook", new[] { "CourseID" });
        DropTable("dbo.RecommendedBook");
    }
}

当迁移失败时,您有两种选择: 在表'RecommendedBook'上引入FOREIGN KEY约束'FK_dbo.RecommendedBook_dbo.Department_DepartmentID'可能会导致循环或多个级联路径。指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束。 无法创建约束或索引。请参阅先前的错误。'

这里是通过在迁移文件中将“ cascadeDelete”设置为false然后运行“ update-database”来使用“修改其他FOREIGN KEY约束”的示例。

答案 10 :(得分:1)

现有的答案很棒我只是想补充说我因为不同的原因遇到了这个错误。我想在现有数据库上创建初始EF迁移,但我没有使用 -IgnoreChanges 标志,并在空数据库上应用Update-Database命令(也在现有的失败数据库上)。

相反,当当前的db结构是当前的db结构时,我必须运行此命令:

Add-Migration Initial -IgnoreChanges

db结构中可能存在一个真正的问题,但一次只能拯救世界......

答案 11 :(得分:1)

使外键属性可为空。那会起作用的。

答案 12 :(得分:1)

最简单的方法是,将迁移文件(cascadeDelete: true)编辑为(cascadeDelete: false),然后在Package Manager控制台中分配Update-Database命令之后。如果最后一次迁移有问题,那么可以。否则,请检查您以前的迁移历史记录,复制这些内容,然后粘贴到您的最后一个迁移文件中,然后再执行相同的操作。它对我来说非常有效。

答案 13 :(得分:1)

这听起来很奇怪,我不知道为什么,但在我的情况下,因为我的ConnectionString使用“。”而发生了这种情况。在“数据源”属性中。一旦我将其改为“localhost”,它就像一个魅力。不需要其他任何改变。

答案 14 :(得分:0)

上述解决方案均不适合我。我必须做的是对不需要的外键(或非空列键)使用可空的int(int?),然后删除我的一些迁移。

首先删除迁移,然后尝试使用nullable int。

问题既是修改又是模型设计。不需要更改代码。

答案 15 :(得分:0)

.NET Core 中我玩了所有上层答案 - 但没有任何成功。 我在数据库结构中做了很多更改,并且每次都添加了尝试update-database的新迁移,但收到了相同的错误。

然后我一个接一个地开始remove-migration,直到程序包管理器控制台让我异常:

  

迁移&#39; 20170827183131 _ ***&#39;已经应用于数据库

之后,我添加了新迁移(add-migration)和update-database 成功

所以我的建议是:清除所有临时迁移,直到你当前的数据库状态。

答案 16 :(得分:-1)

这就是我所做的。这将适用于所有循环关系,因此您不必在OnModelCreating内部一一指定。

步骤

  1. 删除上一次迁移(删除dotnet ef迁移)
  2. 在您的OnModelCreating中添加以下代码段

var cascadeFKs = modelBuilder.Model.GetEntityTypes()
    .SelectMany(t => t.GetForeignKeys())
    .Where(fk => !fk.IsOwnership && fk.DeleteBehavior == DeleteBehavior.Cascade);

foreach (var fk in cascadeFKs)
    fk.DeleteBehavior = DeleteBehavior.Restrict;

  1. 创建新迁移
  2. 更新数据库

这帮了我大忙。

答案 17 :(得分:-1)

我遇到了同样的问题,坚持了很长时间。以下步骤救了我。 遍历约束并将 onDelete ReferentialAction Cascade

更改为 NoAction
  constraints: table =>
  {
      table.PrimaryKey("PK_table1", x => x.Id);
      table.ForeignKey(
         name: "FK_table1_table2_table2Id",
         column: x => x.table2Id,
         principalTable: "table2",
         principalColumn: "Id",
         onDelete: ReferentialAction.NoAction);
  });