programing

외래 키 제약 조건을 도입하면 주기 또는 다중 캐스케이드 경로가 발생할 수 있습니다. 그 이유는 무엇입니까?

bestprogram 2023. 5. 27. 12:04

외래 키 제약 조건을 도입하면 주기 또는 다중 캐스케이드 경로가 발생할 수 있습니다. 그 이유는 무엇입니까?

저는 한동안 이것과 씨름하고 있었는데 무슨 일이 일어나고 있는지 잘 모르겠습니다.측면(일반적으로 2개)을 포함하는 카드 엔티티가 있으며 카드와 측면 모두 스테이지가 있습니다.EF Code를 사용하고 있는데 마이그레이션이 실패하고 다음 오류가 발생했습니다.

외국인 키 제약 조건 'FK_dbo'를 소개합니다.Sides_dbo.테이블 'Sides'의 Cards_CardId'는 주기 또는 다중 캐스케이드 경로를 발생시킬 수 있습니다.ON DELETE NO ACTION 또는 ON UPDATE NO ACTION을 지정하거나 다른 외래 키 제약 조건을 수정합니다.

다음은 내 카드 엔티티:

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

}

그리고 여기 제 Stage 실체가 있습니다.

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에는, 는 Side 수에, StageId에 된 열Side테이블.

이것은 효과가 있지만, 지금 내 지원서 전체에서, 어떤 언급도.Stage을 합니다.SideId어떤 경우에는 전혀 관련이 없는 경우도 있습니다.가능하면 참조 속성으로 스테이지 클래스를 오염시키지 않고 위의 스테이지 클래스에 기반한 속성을 내 및 엔티티에 제공하고 싶습니다.내가 뭘 잘못하고 있는 거지?

ㅠㅠStage모든 일대일 관계가 필요합니다.Stage.is 에 의해 합니다. 여기에는 다음과 같은 항목이 포함됩니다.즉, 만약 당신이 삭제한다면,Stage

  • 삭제는 직접 캐스케이드됩니다.Side
  • 삭제는 직접 캐스케이드됩니다.Card그리고 그 이유는Card그리고.Side된 상태에서 합니다. 계단식 는 기적본으다삭상필다일관유니지합계를대일한요서태에에서 됩니다. 그러면 계단식 삭제가 실행됩니다.CardSide

두 개의 단계적 삭제 경로가 있습니다.StageSide예외가 발생합니다.

당신은 다음 중 하나를 만들어야 합니다.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);

저는 다른 사람들과 순환 관계가 있는 테이블을 가지고 있었는데 같은 오류가 발생했습니다.그것은 무효화되지 않은 외부 키에 관한 것으로 밝혀졌습니다.키가 null이 아닐 경우 관련 개체를 삭제해야 하며 순환 관계에서는 이를 허용하지 않습니다.따라서 null 가능한 외래 키를 사용합니다.

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

EF core에서 어떻게 해야 하는지 알고 싶은 사람:

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

EF7 모델에서 EF6 버전으로 마이그레이션할 때 많은 엔티티에서 이 오류가 발생했습니다.각 엔티티를 한 번에 하나씩 검토할 필요가 없었기 때문에 다음을 사용했습니다.

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

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

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

.NET Core에서 나는 onDelete 옵션을 ReferencialAction으로 변경했습니다.액션 없음

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

저도 이 문제가 있었는데, 비슷한 스레드의 답변으로 즉시 해결했습니다.

저의 경우, 키 삭제에 대한 종속 레코드를 삭제하고 싶지 않았습니다.이 경우 마이그레이션의 부울 값을 false로 변경하기만 하면 됩니다.

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

이 컴파일러 오류를 발생시키는 관계를 만들고 있지만 단계적 삭제를 유지하려면 관계에 문제가 있을 수 있습니다.

제가 고쳤어요.마이그레이션을 추가할 때 Up() 메서드에는 다음과 같은 행이 있습니다.

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

cascadeDelete를 끝에서 삭제하면 작동합니다.

단지 문서화 목적으로, 미래에 올 누군가에게, 이 일은 이렇게 간단하게 해결될 수 있고, 이 방법으로, 당신은 한 번 비활성화된 방법을 할 수 있고, 당신의 방법에 정상적으로 접근할 수 있습니다.

컨텍스트 데이터베이스 클래스에 이 메서드를 추가합니다.

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

.NET Core에서 저는 모든 상위 답변을 사용했지만 성공하지 못했습니다.저는 DB 구조에 많은 변화를 주었고 매번 새로운 마이그레이션을 추가했습니다.update-database하지만 같은 오류를 받았습니다.

그리고 나서 저는 하기 시작했습니다.remove-migration패키지 관리자 콘솔에서 예외를 발생시킬 까지 하나씩:

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

에 새로운 migration)을 했습니다.add-migration및 ) 및update-database

따라서 현재 DB 상태가 될 때까지 임시 마이그레이션을 모두 삭제하는 것이 좋습니다.

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

마이그레이션이 실패하면 몇 가지 옵션이 제공됩니다. '외국인 키 제약 조건 소개'FK_dbo.추천 Book_dbo.부서_부서'RecommendedBook' 테이블의 'ID'는 주기 또는 다중 캐스케이드 경로를 발생시킬 수 있습니다.ON DELETE NO ACTION 또는 ON UPDATE NO ACTION을 지정하거나 다른 외래 키 제약 조건을 수정합니다.제약 조건 또는 인덱스를 만들 수 없습니다.이전 오류를 참조하십시오.'

다음은 마이그레이션 파일에서 'cascadeDelete'를 false로 설정한 다음 'update-database'를 실행하여 '다른 FOREGINAL KEY 제약 조건 수정'을 사용하는 예입니다.

외부 키 특성을 null로 설정합니다.그건 효과가 있을거에요.

이것은 이상하게 들리고 이유는 모르겠지만, 제 경우에는 ConnectionString이 "data source" 특성에 "."를 사용했기 때문에 발생했습니다.일단 제가 "localhost"로 바꾸자 그것은 매력적으로 작동했습니다.다른 변경은 필요하지 않았습니다.

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

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

Add-Migration Initial -IgnoreChanges

db 구조에 진짜 문제가 있을 수 있지만 한 번에 한 단계씩 세상을 구합니다.

.NET 5 < 및 .NET Core 2.0 <에서 사용할 수 있습니다..OnDelete(DeleteBehavior.Restrict)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://learn.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key#join-entity-type-configuration

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

이렇게 하려면 여러 관계를 직접 제거해야 합니다. 그렇지 않으면 상위 엔터티를 제거할 때 다음 오류가 표시됩니다.

엔티티 유형 "과 " 사이의 연결이 끊어졌지만 외부 키가 null이 아니기 때문에 관계가 필수로 표시되거나 암시적으로 필요합니다.필요한 관계가 끊겼을 때 종속/하위 엔티티를 삭제해야 하는 경우 계단식 삭제를 사용하도록 관계를 구성합니다.'DbContextOptionsBuilder'를 사용해 보십시오.주요 값을 보려면 'SensitiveDataLogging' 사용

다음을 사용하여 이 문제를 해결할 수 있습니다.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://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.deletebehavior?view=efcore-5.0

앞서 언급한 어떤 해결책도 저에게 효과가 없었습니다.제가 해야 할 일은 필요하지 않은(또는 null이 아닌) 외부 키에 nullable int(int?)를 사용한 다음 마이그레이션 중 일부를 삭제하는 것이었습니다.

마이그레이션을 삭제하는 것으로 시작한 다음 nullable int를 시도합니다.

문제는 수정과 모델 설계였습니다.코드를 변경할 필요가 없었습니다.

파일 (cascadeDelete: true)안으로(cascadeDelete: false)그런 다음 Package Manager 콘솔에서 Update-Database 명령을 할당한 후 마지막 마이그레이션에 문제가 있는 경우 문제가 없습니다.그렇지 않으면 이전 마이그레이션 기록을 확인하고, 해당 항목을 복사한 후 마지막 마이그레이션 파일에 붙여넣은 다음 동일한 작업을 수행합니다.저한테 딱 맞습니다.

당신의 DataContext.cs 에 이것을 추가할 수 있습니다, 이것은 저에게 효과가 있습니다...

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

저는 같은 문제에 부딪혀서 오랫동안 꼼짝 못했습니다.다음 단계가 저를 구했습니다.제약 조건을 살펴보고 참조 액션 삭제를 캐스케이드에서 액션 없음으로 변경합니다.

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

언급URL : https://stackoverflow.com/questions/17127351/introducing-foreign-key-constraint-may-cause-cycles-or-multiple-cascade-paths