引入FOREIGN KEY约束可能会导致循环或多个级联path – 为什么?
我一直在摔跤了一段时间,不能完全弄清楚发生了什么。 我有一个包含边的卡实体(通常是2) – 卡和边都有一个阶段。 我正在使用EF Codefirst迁移,并且迁移失败,出现此错误:
在表“边”上引入FOREIGN KEY约束“FK_dbo.Sides_dbo.Cards_CardId”可能会导致循环或多个级联path。 指定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
桌。
这是SideId
,但现在在我的应用程序中,任何对Stage
引用都包含一个SideId
,在某些情况下,这是完全不相关的。 我想给我的Card
和Side
实体一个基于上述Stage类的Stage属性,如果可能的话,不用污染的引用属性的阶段类 …我做错了什么?
由于Stage
是所有Stage
所涉及的一对多关系,因此默认情况下会启用级联删除。 这意味着,如果您删除Stage
实体
- 删除将直接级联到
Side
- 删除将直接级联到
Card
,因为Card
和Side
有一个必要的一对多的关系,级联删除默认启用,然后它将从Card
到Side
级联
所以,你有两个级联删除path从Stage
到Side
– 这会导致exception。
您必须使Stage
在至less一个实体中可选(即,从Stage
属性中删除[Required]
属性),或者使用Fluent API禁用级联删除(数据标注不可能):
modelBuilder.Entity<Card>() .HasRequired(c => c.Stage) .WithMany() .WillCascadeOnDelete(false); modelBuilder.Entity<Side>() .HasRequired(s => s.Stage) .WithMany() .WillCascadeOnDelete(false);
我有一个表与其他人有循环关系,我得到了同样的错误。 原来是关于不可空的外键。 如果键不可为空,相关对象必须被删除,循环关系不允许。 所以使用可空的外键。
[ForeignKey("StageId")] public virtual Stage Stage { get; set; } public int? StageId { get; set; }
当我从EF7模型迁移到EF6版本时,出现了很多实体的错误。 我不想一次一个地去查看每个实体,所以我用了:
builder.Conventions.Remove<ManyToManyCascadeDeleteConvention>(); builder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
您可以将cascadeDelete设置为false或true(在您的迁移Up()方法中)。 取决于你的要求。
AddForeignKey("dbo.Stories", "StatusId", "dbo.Status", "StatusID", cascadeDelete: false);
有人想知道如何在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.....
我也有这个问题,我用类似的线程回答了这个问题
在我的情况下,我不想删除关键删除的从属logging。 如果在您的情况下,只是将迁移中的布尔值更改为false:
AddForeignKey("dbo.Stories", "StatusId", "dbo.Status", "StatusID", cascadeDelete: false);
机会是,如果你正在创build关系抛出这个编译器错误,但是要维护级联删除; 你的关系有问题。
这听起来很奇怪,我不知道为什么,但在我的情况下,因为我的ConnectionString使用“。 在“数据源”属性中。 一旦我将其改为“本地主机”,它就像一个魅力。 没有其他变化是需要的。
在.NET Core中,我玩的是所有较高的答案 – 但没有任何成功。 我在数据库结构上进行了很多修改,并且每次都添加新的迁移尝试update-database
,但却收到了同样的错误。
然后,我开始remove-migration
,直到Package Manager Console向我抛出exception:
迁移“20170827183131 _ ***”已经应用于数据库
之后,我成功添加了新的迁移( add-migration
)和update-database
所以我的build议是:清除所有的临时迁移,直到你当前的数据库状态。
在.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); });