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경우에 따라 전혀 관련이 없습니다. 난 그냥 내주고 싶습니다 CardSide단체 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때문 CardSide이 삭제 다시는 그때부터 단계적으로합니다 기본적으로 활성화 계단식으로 일대 다 관계를 필요 CardSide

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