362

我已经为此苦苦挣扎了一段时间,无法完全弄清楚发生了什么。我有一个卡片实体,其中包含边(通常是 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; }

}

这是我的舞台实体:

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包含 a 的引用SideId,在某些情况下是完全不相关的。 如果可能的话,我只想给我CardSide实体一个Stage基于上述 Stage 类的属性,而不用引用属性污染舞台类......我做错了什么?

4

19 回答 19

437

因为Stagerequired,所以所有涉及的一对多关系Stage都将默认启用级联删除。这意味着,如果您删除一个Stage实体

  • 删除将直接级联到Side
  • 删除将直接级联到Card并且因为Card并且Side与默认启用的级联删除具有必需的一对多关系,然后它将级联CardSide

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

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

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

modelBuilder.Entity<Side>()
    .HasRequired(s => s.Stage)
    .WithMany()
    .WillCascadeOnDelete(false);
于 2013-06-15T20:14:52.507 回答
83

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

[ForeignKey("StageId")]
public virtual Stage Stage { get; set; }
public int? StageId { get; set; }
于 2015-07-03T15:05:35.227 回答
47

任何想知道如何在 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.....
于 2016-12-21T18:10:01.783 回答
27

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

builder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
builder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
于 2016-04-15T07:19:23.767 回答
24

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

AddForeignKey("dbo.Stories", "StatusId", "dbo.Status", "StatusID", cascadeDelete: false);
于 2015-02-21T17:21:28.667 回答
18

在 .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);
            });
于 2017-10-10T01:24:47.357 回答
11

我也有这个问题,我用类似线程的这个答案立即解决了

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

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

很有可能,如果您正在创建会引发此编译器错误但确实想要维护级联删除的关系;你的人际关系有问题。

于 2014-11-19T14:46:04.407 回答
8

我解决了这个问题。当你添加迁移时,在 Up() 方法中会有这样的一行:

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

如果您只是从最后删除 cascadeDelete 它将起作用。

于 2018-03-30T22:00:08.320 回答
7

只是为了文档的目的,对于未来的人来说,这件事可以像这样简单地解决,并且使用这种方法,您可以执行一次禁用的方法,并且您可以正常访问您的方法

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

protected override void OnModelCreating(DbModelBuilder modelBuilder) {
    modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
于 2018-04-01T12:53:36.037 回答
2

使您的外键属性可以为空。那可行。

于 2020-04-04T19:29:29.553 回答
2

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

remove-migration然后我一一开始,直到包管理器控制台向我抛出异常:

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

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

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

于 2017-08-27T21:07:15.573 回答
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”。

于 2020-02-04T09:42:50.733 回答
1

简单的方法是,在包管理器控制台中分配 Update-Database 命令后,将迁移文件编辑(cascadeDelete: true)(cascadeDelete: false)then 中。如果上次迁移有问题,那么好吧。否则检查你之前的迁移历史,复制这些东西,粘贴到你的最后一个迁移文件中,然后做同样的事情。它非常适合我。

于 2019-10-03T05:20:12.190 回答
1

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

相反,当当前数据库结构是当前数据库结构时,我必须运行此命令:

Add-Migration Initial -IgnoreChanges

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

于 2017-12-15T12:21:36.957 回答
1

这听起来很奇怪,我不知道为什么,但就我而言,这是因为我的 ConnectionString 使用的是“。” 在“数据源”属性中。一旦我将它更改为“localhost”,它就像一个魅力。无需进行其他更改。

于 2015-12-07T15:29:34.617 回答
1

.OnDelete(DeleteBehavior.Restrict)在 .NET 5 < 和 .NET Core 2.0 <中,您可以OnModelCreating像 @Nexus23 一样使用答案,但您不需要为每个模型禁用级联。

连接实体类型配置多对多的示例:

internal class MyContext : DbContext
{
    public MyContext(DbContextOptions<MyContext> options)
        : base(options)
    {
    }

    public DbSet<Post> Posts { get; set; }
    public DbSet<Tag> Tags { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Post>()
            .HasMany(p => p.Tags)
            .WithMany(p => p.Posts)
            .UsingEntity<PostTag>(
                j => j
                    .HasOne(pt => pt.Tag)
                    .WithMany(t => t.PostTags)
                    .HasForeignKey(pt => pt.TagId)
                    .OnDelete(DeleteBehavior.Restrict),
                j => j
                    .HasOne(pt => pt.Post)
                    .WithMany(p => p.PostTags)
                    .HasForeignKey(pt => pt.PostId)
                    .OnDelete(DeleteBehavior.Restrict),
                j =>
                {
                    j.Property(pt => pt.PublicationDate).HasDefaultValueSql("CURRENT_TIMESTAMP");
                    j.HasKey(t => new { t.PostId, t.TagId });
                });
    }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public ICollection<Tag> Tags { get; set; }
    public List<PostTag> PostTags { get; set; }
}

public class Tag
{
    public string TagId { get; set; }

    public ICollection<Post> Posts { get; set; }
    public List<PostTag> PostTags { get; set; }
}

public class PostTag
{
    public DateTime PublicationDate { get; set; }

    public int PostId { get; set; }
    public Post Post { get; set; }

    public string TagId { get; set; }
    public Tag Tag { get; set; }
}

资料来源:

https://docs.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key#join-entity-type-configuration

https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.deletebehavior?view=efcore-5.0

这确实需要您自己删除多对多关系,否则在删除父实体时会收到以下错误:

实体类型 '' 和 '' 之间的关联已被切断,但该关系要么被标记为必需,要么被隐含地要求,因为外键不可为空。如果在切断所需关系时应删除依赖/子实体,请将关系配置为使用级联删除。考虑使用“DbContextOptionsBuilder.EnableSensitiveDataLogging”查看键值

您可以通过使用来解决此问题,DeleteBehavior.ClientCascade这将允许 EF 对加载的实体执行级联删除。

internal class MyContext : DbContext
{
    public MyContext(DbContextOptions<MyContext> options)
        : base(options)
    {
    }

    public DbSet<Post> Posts { get; set; }
    public DbSet<Tag> Tags { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Post>()
            .HasMany(p => p.Tags)
            .WithMany(p => p.Posts)
            .UsingEntity<PostTag>(
                j => j
                    .HasOne(pt => pt.Tag)
                    .WithMany(t => t.PostTags)
                    .HasForeignKey(pt => pt.TagId)
                    .OnDelete(DeleteBehavior.Cascade),
                j => j
                    .HasOne(pt => pt.Post)
                    .WithMany(p => p.PostTags)
                    .HasForeignKey(pt => pt.PostId)
                    .OnDelete(DeleteBehavior.ClientCascade),
                j =>
                {
                    j.Property(pt => pt.PublicationDate).HasDefaultValueSql("CURRENT_TIMESTAMP");
                    j.HasKey(t => new { t.PostId, t.TagId });
                });
    }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public ICollection<Tag> Tags { get; set; }
    public List<PostTag> PostTags { get; set; }
}

public class Tag
{
    public string TagId { get; set; }

    public ICollection<Post> Posts { get; set; }
    public List<PostTag> PostTags { get; set; }
}

public class PostTag
{
    public DateTime PublicationDate { get; set; }

    public int PostId { get; set; }
    public Post Post { get; set; }

    public string TagId { get; set; }
    public Tag Tag { get; set; }
}

https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.deletebehavior?view=efcore-5.0

于 2021-09-18T10:43:44.800 回答
0

您可以在 DataContext.cs 中添加它,这对我有用...

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
    modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
}
于 2021-09-23T14:12:43.107 回答
0

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

首先删除迁移,然后尝试可以为空的 int。

问题在于修改和模型设计。无需更改代码。

于 2018-02-06T15:29:12.313 回答
-3

我遇到了同样的问题并坚持了很长时间。以下步骤救了我。通过约束并将onDelete ReferentialActionCascade更改为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);
  });
于 2020-11-08T07:56:52.157 回答