首页 文章

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

提问于
浏览
236

我已经和它搏斗了一段时间,并且无法弄清楚发生了什么 . 我有一个卡片实体,其中包含Sides(通常为2个) - 卡片和侧面都有一个舞台 . 我正在使用EF Codefirst迁移,并且迁移失败并出现此错误:

在表'Sides'上引入FOREIGN KEY约束'FK_dbo.Sides_dbo.Cards_CardId'可能会导致循环或多个级联路径 . 指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束 .

这是我的 Card 实体:

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; }

}

这是我的 Stage 实体:

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 ,在某些情况下完全无关紧要 . I'd like to just give my Card and Side entities a Stage property based on the above Stage class without polluting the stage class with reference properties if possible ......我做错了什么?

13 回答

  • 5

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

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

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

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

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

    由于 Stage 是必需的,因此涉及 Stage 的所有一对多关系将默认启用级联删除 . 这意味着,如果删除 Stage 实体

    • 删除将直接级联到 Side

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

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

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

    modelBuilder.Entity<Card>()
        .HasRequired(c => c.Stage)
        .WithMany()
        .WillCascadeOnDelete(false);
    
    modelBuilder.Entity<Side>()
        .HasRequired(s => s.Stage)
        .WithMany()
        .WillCascadeOnDelete(false);
    
  • 318

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

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

    Add-Migration Initial -IgnoreChanges
    

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

  • 0

    在.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);
                });
    
  • 14

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

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

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

  • 20

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

    首先删除迁移,然后尝试nullable int .

    问题既是修改又是模型设计 . 无需更改代码 .

  • 0

    我也有这个问题,我立即用this answer from a similar thread解决了

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

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

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

  • 4

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

    然后我一个接一个地开始 remove-migration ,直到Package Manager Console引发异常:

    迁移'20170827183131 _ ***'已应用于数据库

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

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

  • 3

    我修好了 . 添加迁移时,在Up()方法中会有像这样的一条线:

    .ForeignKey("dbo.Members", t => t.MemberId, cascadeDelete:True)
    

    如果你只是从最后删除cascadeDelete它将工作 .

  • 1

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

    protected override void OnModelCreating(ModelBuilder modelBuilder)
                {
                    foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
                    {
                        relationship.DeleteBehavior = DeleteBehavior.Restrict;
                    }
               ..... rest of the code.....
    
  • 1

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

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

相关问题