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

이상한 점은 스테이지 클래스에 다음을 추가하면 다음과 같습니다.

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

내 사이드 ​​클래스에 StageId열이 추가 된 것을 볼 수 있습니다.Side 테이블에 있습니다.

이것은 작동하지만 이제는 내 응용 프로그램 전체에서에 대한 모든 참조가 Stage포함되어 있으며 SideId경우에 따라 전혀 관련이 없습니다. 난 그냥 내주고 싶습니다 Card및 Side단체 Stage참조 속성 가능한 경우와 무대 클래스 오염없이 무대 위의 클래스를 기반으로 재산을 ... 내가 잘못을하고있는 중이 야 무엇을?


7
참조에서 null 값을 허용하여 계단식 삭제를 비활성화하십시오 ... Side클래스 에서 Nullable 정수를 추가하고 [Required]속성을 제거하십시오 =>public int? CardId { get; set; }
— Jaider

2
EF Core에서는 DeleteBehavior.Restrict또는로 계단식 삭제를 비활성화해야합니다 DeleteBehavior.SetNull.
— Sina Lotfi

답변:


371

Stageis가 필요 하기 때문에 Stage관련된 모든 일대 다 관계에는 기본적으로 계단식 삭제가 사용됩니다. Stage엔터티 를 삭제하면

  • 삭제는 Side
  • 삭제가 직접 연쇄 것 Card때문 Card와 Side이 삭제 다시는 그때부터 단계적으로합니다 기본적으로 활성화 계단식으로 일대 다 관계를 필요 Card로Side

따라서 두 개의 계단식 삭제 경로가 있습니다 Stage.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);

2
고마워요. 위에서 설명한대로 유창한 API를 사용하면 다른 필드의 계단식 삭제 동작이 유지됩니까? 예를 들어 카드를 삭제할 때 여전히 측면을 삭제해야합니다.
— SB2055

1
@ SB2055 : 예 Stage. 의 관계에만 영향을 미칩니다 . 다른 관계는 변하지 않습니다.
— Slauma 2016 년

2
어떤 속성이 오류를 일으키는 지 알 수있는 방법이 있습니까? 나도 같은 문제를 겪고 있고, 수업을 살펴보면 사이클이 어디에 있는지 알 수 없다
— Rodrigo Juarez

4
이것이 구현에 제한이 있습니까? Stage삭제가 Side직접적으로 그리고 모두를 통해 계단식으로 내려 오는 것으로 나에게 괜찮은 것 같습니다Card
— aaaaaa

1
CascadeOnDelete를 false로 설정했다고 가정하십시오. 그런 다음 카드 레코드 중 하나와 관련된 단계 레코드를 제거했습니다. Card.Stage (FK)는 어떻게됩니까? 동일하게 유지됩니까? 아니면 Null로 설정되어 있습니까?
— ninbit December

61

나는 다른 사람들과 순환 관계가있는 테이블을 가지고 있었고 같은 오류가 발생했습니다. 그것은 nullable이 아닌 외래 키에 관한 것입니다. 키가 널 입력 가능하지 않은 경우 관련 오브젝트를 삭제해야하며 순환 관계에서는이를 허용하지 않습니다. 따라서 널 입력 가능 외래 키를 사용하십시오.

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

5
[필수] 태그를 제거했지만 또 다른 중요한 것은 null을 허용 하는 int?대신 사용하는 것입니다 int.
— VSB

1
캐스케이드 삭제를 끄는 여러 가지 방법을 시도했지만 아무것도 작동하지 않았습니다.
— ambog36

5
Stage를 null로 설정하지 않으려면이 작업을 수행하지 않아야합니다 (원래 질문에서 스테이지는 필수 필드 임).
— cfwall

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 버전으로 마이그레이션 할 때 많은 엔티티에 대해이 오류가 발생했습니다. 한 번에 하나씩 각 엔티티를 통과하고 싶지는 않았으므로 다음을 사용했습니다.

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

2
이것은 DbContext에서 상속 된 클래스 (예 : OnModelCreating 메서드)에 추가해야합니다. 빌더 형 DbModelBuilder이다
— CodingYourLife

이것은 나를 위해 일했다. .NET 4.7, EF 6. 하나의 걸림돌은 오류가 발생했기 때문에 이러한 규칙을 제거한 상태에서 마이그레이션 스크립트로 다시 생성 할 때 도움이되지 않는 것으로 보입니다. "-Force"로 "Add-Migration"을 실행하면 모든 항목이 지워지고 위의 규칙을 포함하여 다시 작성됩니다. 문제가 해결되었습니다.
— James Joyce

그것들은 .net 코어에 존재하지 않습니다.
— jjxtra


20

마이그레이션 Up () 메서드에서 cascadeDelete를 false 또는 true로 설정할 수 있습니다. 요구 사항에 따라 다릅니다.

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

2
@Mussakkhir 답변 주셔서 감사합니다. 당신의 길은 매우 우아하고 이상입니다-더 정확하고 직접 직면 한 문제를 겨냥합니다!
— 노짐 투 라쿠 로프

UP외부 작업으로 메소드가 수정 될 수 있음을 잊지 마십시오 .
— Dementic

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)

일단 cascadeDelete를 삭제하면 작동합니다.


5

문서화 목적으로, 미래에 온 누군가에게이 문제는 이렇게 간단하게 해결 될 수 있으며,이 방법을 사용하면 한 번 비활성화 된 방법을 수행 할 수 있으며 정상적으로 방법에 액세스 할 수 있습니다

이 메소드를 컨텍스트 데이터베이스 클래스에 추가하십시오.

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

1

이상하게 들리고 왜 그런지 모르겠지만 내 경우에는 ConnectionString이 "."을 사용했기 때문에 발생했습니다. "데이터 소스"속성에서. "localhost"로 변경하면 매력처럼 작동했습니다. 다른 변경은 필요하지 않았습니다.


1

에서 .NET 핵심 나는 모든 상위 답변을했다 -하지만 어떤 성공없이. DB 구조를 많이 변경했으며 매번 새로운 마이그레이션 시도를 추가했습니다.update-database 동일한 오류가 발생했습니다.

그런 다음 패키지 관리자 콘솔에서 예외가 발생할 remove-migration때까지 하나씩 시작했습니다 .

'20170827183131 _ ***'마이그레이션이 이미 데이터베이스에 적용되었습니다

그 후 새 마이그레이션 ( add-migration)을 추가 하고update-database 성공적으로

따라서 내 제안은 다음과 같습니다. 현재 DB 상태가 될 때까지 모든 임시 마이그레이션을 지우십시오.


1

기존 답변은 훌륭합니다. 다른 이유로 인해이 오류가 발생했다고 덧붙였습니다. 기존 DB에서 초기 EF 마이그레이션을 만들고 싶었지만 -IgnoreChanges를 사용하지 않았습니다. 플래그를 빈 데이터베이스에 Update-Database 명령을 적용했습니다 (기존의 실패에도 적용).

대신 현재 db 구조가 현재 구조 일 때이 명령을 실행해야했습니다.

Add-Migration Initial -IgnoreChanges

db 구조에 실제 문제가있을 수 있지만 한 번에 한 단계 씩 세계를 구하십시오 ...


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

마이그레이션이 실패하면 다음과 같은 두 가지 옵션이 제공됩니다. '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 제약 조건 수정'을 사용하는 예입니다.


0

위에서 언급 한 솔루션 중 어느 것도 나를 위해 일하지 않았습니다. 내가해야 할 일은 필요하지 않은 외래 키 (또는 null이 아닌 열 키)에 nullable int (int?)를 사용한 다음 일부 마이그레이션을 삭제하는 것입니다.

마이그레이션을 삭제하여 시작한 다음 널 입력 가능 int를 시도하십시오.

문제는 수정과 모델 디자인이었습니다. 코드 변경이 필요하지 않았습니다.


-1

외래 키 속성을 nullable로 설정하십시오. 작동합니다.


1
질문 아래의 의견에 대한 답변은 거기에서 자세히 설명해주십시오
— Kostia Mololkin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.