FOREIGN KEY制約を導入すると、サイクルまたは複数のカスケードパスが発生する可能性があります。なぜですか?


295

私はしばらくこれに取り組んできましたが、何が起こっているのかよくわかりません。サイド(通常は2)を含むカードエンティティがあり、カードとサイドの両方にステージがあります。EF Codefirst移行を使用していますが、このエラーで移行が失敗します。

テーブル 'Sides'にFOREIGN KEY制約 'FK_dbo.Sides_dbo.Cards_CardId'を導入すると、サイクルまたは複数のカスケードパスが発生する可能性があります。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; }
}

これが私のサイドエンティティです:

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_StageIdCards(期待どおり/望ましい)に追加されていることがわかりSidesますが、Stage(予期されていない)への参照が含まれていません。

追加した場合

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

SideクラスではStageIdSideテーブルに列が追加されています。

これは機能していますが、私のアプリケーション全体で、への参照にStageはが含まれてSideIdいますが、場合によってはまったく関係ありません。 可能な場合は参照プロパティでステージクラスを汚染することなく、上記のステージクラスに基づくプロパティを自分CardSideエンティティに与えたいのStageですが、どうすればよいですか。


7
参照にnull値を許可することでカスケード削除を無効にする... SideクラスでNullable整数を追加し、[Required]属性を削除する=>public int? CardId { get; set; }
Jaider

2
EF Coreでは、DeleteBehavior.Restrictまたはでカスケード削除を無効にする必要がありDeleteBehavior.SetNullます。
Sina Lotfi、

回答:


371

Stage必須であるため、関係するすべての1 Stage対多の関係では、カスケード削除がデフォルトで有効になります。Stageエンティティを削除すると

  • 削除は直接にカスケードされます Side
  • 直接に削除させていただきますカスケードCardと理由CardSide、それは、その後からカスケードされます再びデフォルトで有効になって削除カスケードで必要な1対多の関係を持っているCardSide

だから、あなたは2から削除パスをカスケード接続する必要がありStageSide例外が発生します- 。

あなたはどちらかにする必要がありますStageエンティティの少なくとも一方で、オプション(つまり削除[Required]から属性Stageプロパティ)または無効カスケードは、(データ注釈を含むことはできません)流暢APIを削除します。

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

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

2
Slaumaに感謝します。上記で示したようにFluent APIを使用する場合、他のフィールドはカスケード削除動作を保持しますか?たとえば、カードが削除されたときに、サイドを削除する必要があります。
SB2055 2013年

1
@ SB2055:はい、それはからの関係にのみ影響しStageます。他の関係は変更されません。
Slauma 2013年

2
プロパティがエラーを引き起こしていることを知る方法はありますか?私は同じ問題を抱えて、かつサイクルです私は見can't私のクラスで探しています
ロドリゴ・フアレス

4
これは実装の制限ですか?直接およびaを介しStageてカスケードする削除のために私には問題ないSideCard
思います

1
CascadeOnDeleteをfalseに設定するとします。次に、カードレコードの1つに関連するステージレコードを削除しました。Card.Stage(FK)はどうなりますか?それは同じままですか?それともヌルに設定されていますか?
ninbit

61

他のユーザーと循環的な関係を持つテーブルがあり、同じエラーが発生しました。null可能ではなかった外部キーについてであることが判明しました。キーがnullにできない場合は、関連オブジェクトを削除する必要があり、循環リレーションはそれを許可しません。したがって、null許容の外部キーを使用してください。

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

5
[必須]タグを削除しましたが、もう1つの重要なことは、それをnull可能にするint?代わりに使用することでしたint
VSB 2016年

1
カスケード削除をオフにするさまざまな方法を試しましたが、何も機能しませんでした。
ambog36 2016年

5
ステージをnullに設定したくない場合は、これを行わないでください(ステージは元の質問では必須フィールドでした)。
cfwall 2017年

35

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.....

3
これにより、すべての関係でカスケード削除がオフになります。一部のユースケースでは、カスケード削除が望ましい機能である場合があります。
Blaze

15
また、builder.HasOne(x => x.Stage).WithMany().HasForeignKey(x => x.StageId).OnDelete(DeleteBehavior.Restrict);
ビスケット

@Biscuits拡張メソッドが時間の経過とともに変化したか、builder _ .Entity<TEntity>() _以前に忘れていましたHasOne() 場合に呼び出すことができます...
ViRuSTriNiTy

1
@ViRuSTriNiTy、私のスニペットは2歳です。しかし、私はあなたが正しいと思います-最近では、実装することを選択した場合にそうなりますIEntityTypeConfiguration<T>builder.Entity<T>当時、その方法を見た覚えはありませんが、私は間違っている可能性があります。それでも、どちらも機能します:)
ビスケット

21

EF7モデルからEF6バージョンに移行すると、多くのエンティティでこのエラーが発生しました。各エンティティを一度に1つずつ確認する必要がなかったので、次を使用しました。

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

2
これは、DbContextから継承するクラス(たとえば、OnModelCreatingメソッド)に追加する必要があります。ビルダーのタイプはDbModelBuilderです
CodingYourLife

これでうまくいきました。.NET 4.7、EF 6.障害の1つにエラーが発生したため、これらの規則を削除して移行スクリプトで再生成すると、役に立たないようでした。「-Force」を指定して「Add-Migration」を実行すると、すべてがクリアされ、上記の規則を含めて再構築されました。問題は解決しました...
James Joyce

それらは.netコアには存在しません、同等のものはありますか?
jjxtra


20

カスケードUp()メソッドで、cascadeDeleteをfalseまたはtrueに設定できます。要件によって異なります。

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

2
@Mussakkhir回答ありがとうございます。あなたの方法は非常にエレガントでもっと上手です-それはより正確で、私が直面している問題に直接的を絞っています!
Nozim Turakulov 2016年

UPメソッドが外部操作によって変更される可能性があることを忘れないでください。
認知症

8

.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

私もこの問題がありました、私は同じようなスレッドからのこの答えで即座に解決しました

私の場合、キーの削除に依存するレコードを削除したくありませんでした。これが当てはまる場合は、マイグレーションのブール値をfalseに変更するだけです。

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

このコンパイラエラーをスローする関係を作成しているが、カスケード削除を維持したい場合は、あなたの関係に問題があります。


6

これを直した。マイグレーションを追加すると、Up()メソッドに次のような行があります。

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

カスケード削除を最後から削除するだけで機能します。


5

ドキュメンテーションの目的のためだけに、将来来る人にとって、これはこれと同じくらい簡単に解決でき、この方法を使用すると、一度無効にしたメソッドを実行して、通常の方法でメソッドにアクセスできます

このメソッドをコンテキストデータベースクラスに追加します。

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

1

これは奇妙に聞こえますが、理由はわかりませんが、私の場合、ConnectionStringが "。"を使用していたために起こっていました。「データソース」属性。「localhost」に変更すると、魅力のように機能しました。他の変更は必要ありませんでした。


1

では、.NETのコアが、任意の成功なし-私はすべての上位答えを果たしました。私はDB構造に多くの変更を加え、毎回新しい移行を追加しようとしましたupdate-databaseたが、同じエラーを受け取りました。

次にremove-migrationパッケージマネージャーコンソールが例外をスローするまで、1つずつ開始しました。

移行 '20170827183131 _ ***'はすでにデータベースに適用されています

その後、私は新しい移行(add-migration)を追加し、update-database 正常に

したがって、私の提案は次のとおりです。現在のDBの状態になるまで、一時的な移行をすべてクリアします。


1

既存の回答は素晴らしいものです。別の理由でこのエラーに遭遇したことを追加したいと思います。既存のDBに初期EF移行を作成したかったのですが、-IgnoreChangesを使用しませんでしたフラグを、空のデータベース(既存のデータベースでも失敗)にUpdate-Databaseコマンドを適用しました。

代わりに、現在のdb構造が現在のものであるときに、このコマンドを実行する必要がありました。

Add-Migration Initial -IgnoreChanges

db構造に実際の問題がある可能性がありますが、一度に1ステップずつ世界を救います...


1

簡単な方法は、パッケージマネージャーコンソールでUpdate-Databaseコマンドを割り当てた後で移行ファイル(cascadeDelete: true)を編集(cascadeDelete: false)し、最後の移行で問題が発生した場合はそれで問題ありません。それ以外の場合は、以前の移行履歴を確認し、それらをコピーして、最後の移行ファイルに貼り付け、その後同じことを行います。それは完璧に機能します。


1
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");
    }
}

移行が失敗した場合、次の2つのオプションが提供されます。ON DELETE NO ACTIONまたはON UPDATE NO ACTIONを指定するか、他のFOREIGN KEY制約を変更します。制約またはインデックスを作成できませんでした。以前のエラーを参照してください。」

次に、移行ファイルで 'cascadeDelete'をfalseに設定して 'modify other FOREIGN KEY constraint'を使用し、 'update-database'を実行する例を示します。


0

前述の解決策のどれも私にとってはうまくいきませんでした。私がしなければならなかったのは、不要な外部キー(またはnullでない列キーではない)にnull許容のint(int?)を使用し、マイグレーションの一部を削除することでした。

マイグレーションを削除することから始めて、nullable intを試します。

問題は修正とモデル設計の両方でした。コードを変更する必要はありませんでした。


-1

外部キー属性をヌル可能にします。うまくいきます。


1
質問の下のコメントでの回答は詳しく説明してください
Kostia Mololkin
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.