엔티티 프레임 워크 코드 첫 번째-동일한 테이블의 두 개의 외래 키


260

방금 EF 코드 사용을 시작했기 때문에이 주제의 초보자입니다.

팀과 경기 사이의 관계를 만들고 싶었습니다.

1 경기 = 2 팀 (홈, 게스트) 및 결과.

그런 모델을 만드는 것이 쉽다고 생각하여 코딩을 시작했습니다.

public class Team
{
    [Key]
    public int TeamId { get; set;} 
    public string Name { get; set; }

    public virtual ICollection<Match> Matches { get; set; }
}


public class Match
{
    [Key]
    public int MatchId { get; set; }

    [ForeignKey("HomeTeam"), Column(Order = 0)]
    public int HomeTeamId { get; set; }
    [ForeignKey("GuestTeam"), Column(Order = 1)]
    public int GuestTeamId { get; set; }

    public float HomePoints { get; set; }
    public float GuestPoints { get; set; }
    public DateTime Date { get; set; }

    public virtual Team HomeTeam { get; set; }
    public virtual Team GuestTeam { get; set; }
}

그리고 나는 예외를 얻는다 :

참조 관계는 순환 참조를 허용하지 않습니다. [구속 조건 이름 = Match_GuestTeam]

같은 테이블에 2 개의 외래 키가있는 모델을 어떻게 만들 수 있습니까?

답변:


296

이 시도:

public class Team
{
    public int TeamId { get; set;} 
    public string Name { get; set; }

    public virtual ICollection<Match> HomeMatches { get; set; }
    public virtual ICollection<Match> AwayMatches { get; set; }
}

public class Match
{
    public int MatchId { get; set; }

    public int HomeTeamId { get; set; }
    public int GuestTeamId { get; set; }

    public float HomePoints { get; set; }
    public float GuestPoints { get; set; }
    public DateTime Date { get; set; }

    public virtual Team HomeTeam { get; set; }
    public virtual Team GuestTeam { get; set; }
}


public class Context : DbContext
{
    ...

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Match>()
                    .HasRequired(m => m.HomeTeam)
                    .WithMany(t => t.HomeMatches)
                    .HasForeignKey(m => m.HomeTeamId)
                    .WillCascadeOnDelete(false);

        modelBuilder.Entity<Match>()
                    .HasRequired(m => m.GuestTeam)
                    .WithMany(t => t.AwayMatches)
                    .HasForeignKey(m => m.GuestTeamId)
                    .WillCascadeOnDelete(false);
    }
}

기본 키는 기본 규칙에 따라 매핑됩니다. 팀에는 두 개의 경기 컬렉션이 있어야합니다. 두 개의 FK가 참조하는 단일 컬렉션을 가질 수 없습니다. 이 자체 참조 다 대다에서는 작동하지 않기 때문에 계단식 삭제없이 일치가 매핑됩니다.


3
두 팀이 한 번만 플레이 할 수 있다면 어떨까요?
ca9163d9 2016 년

4
@ NickW : 매핑이 아닌 응용 프로그램에서 처리해야합니다. 매핑 관점에서 페어는 두 번 (각각 게스트와 홈입니다) 플레이 할 수 있습니다.
Ladislav Mrnka

2
비슷한 모델이 있습니다. 팀이 제거 된 경우 계단식 삭제를 처리하는 올바른 방법은 무엇입니까? INSTEAD OF DELETE 트리거를 작성했지만 더 나은 솔루션이 있는지 확실하지 않습니까? 응용 프로그램이 아닌 DB에서 이것을 처리하고 싶습니다.
Woodchipper

1
@ mrshickadance : 동일합니다. 한 가지 접근법은 유창한 API와 다른 데이터 주석을 사용합니다.
Ladislav Mrnka

1
WillCascadeOnDelete false를 사용하면 팀을 삭제하려는 경우 오류가 발생합니다. 'Team_HomeMatches'AssociationSet의 관계가 '삭제됨'상태입니다. 다중 제약 조건이 주어지면 해당 'Team_HomeMatches_Target'도 '삭제됨'상태 여야합니다.
Rupesh Kumar Tiwari 2016

55

ForeignKey()탐색 속성 에서 속성 을 지정할 수도 있습니다 .

[ForeignKey("HomeTeamID")]
public virtual Team HomeTeam { get; set; }
[ForeignKey("GuestTeamID")]
public virtual Team GuestTeam { get; set; }

그렇게하면 OnModelCreate메소드에 코드를 추가 할 필요가 없습니다


4
어느 쪽이든 같은 예외가 발생합니다.
Jo Smo

11
엔터티에 동일한 유형 (HomeTeam 및 GuestTeam 시나리오와 유사)의 둘 이상의 탐색 속성이 포함 된 경우 예외를 제외하고 모든 경우에 적용되는 외래 키를 지정하는 표준 방법입니다.이 경우 EF는 SQL 생성에 혼란을줍니다. 해결책은 OnModelCreate허용되는 답변과 관계의 양쪽에 대한 두 컬렉션에 따라 코드를 추가 하는 것입니다.
Steven Manuel

나는 언급 된 경우를 제외하고 모든 경우에 onmodelcreating을 사용하고, 데이터 주석 외래 키를 사용하며, 왜 허용되지 않는지 모르겠습니다!
hosam hemaily

48

나는 그것이 몇 년 된 게시물이라는 것을 알고 있으며 위의 해결책으로 문제를 해결할 수 있습니다. 그러나 여전히 필요한 사람을 위해 InverseProperty를 사용하는 것이 좋습니다. 적어도 OnModelCreating에서 아무것도 변경할 필요가 없습니다.

아래 코드는 테스트되지 않았습니다.

public class Team
{
    [Key]
    public int TeamId { get; set;} 
    public string Name { get; set; }

    [InverseProperty("HomeTeam")]
    public virtual ICollection<Match> HomeMatches { get; set; }

    [InverseProperty("GuestTeam")]
    public virtual ICollection<Match> GuestMatches { get; set; }
}


public class Match
{
    [Key]
    public int MatchId { get; set; }

    public float HomePoints { get; set; }
    public float GuestPoints { get; set; }
    public DateTime Date { get; set; }

    public virtual Team HomeTeam { get; set; }
    public virtual Team GuestTeam { get; set; }
}

MSDN의 InverseProperty에 대한 자세한 내용은 https://msdn.microsoft.com/en-us/data/jj591583?f=255&MSPPError=-2147217396#Relationships를 참조하십시오.


1
이 답변에 감사하지만 일치 테이블에서 외래 키 열을 nullable로 만듭니다.
RobHurd

이것은 nullable 컬렉션이 필요한 EF 6에서 나에게 효과적이었습니다.
Pynt

유창한 API를 피하고 싶다면 (다른 이유가 무엇이든간에 #differentdiscussion) 이것은 환상적으로 작동합니다. 필자의 경우 필자는 필드 / 테이블에 PK 문자열이 있기 때문에 "일치"엔터티에 추가 foriegnKey 주석을 포함시켜야했습니다.
DiscipleMichael

1
이것은 나를 위해 크게 일했습니다. Btw. 열을 Null 허용하지 않으려면 [ForeignKey] 특성으로 외래 키를 지정할 수 있습니다. 키가 널 입력 가능하지 않으면 모두 설정됩니다.
Jakub Holovsky

16

당신도 이것을 시도 할 수 있습니다 :

public class Match
{
    [Key]
    public int MatchId { get; set; }

    [ForeignKey("HomeTeam"), Column(Order = 0)]
    public int? HomeTeamId { get; set; }
    [ForeignKey("GuestTeam"), Column(Order = 1)]
    public int? GuestTeamId { get; set; }

    public float HomePoints { get; set; }
    public float GuestPoints { get; set; }
    public DateTime Date { get; set; }

    public virtual Team HomeTeam { get; set; }
    public virtual Team GuestTeam { get; set; }
}

FK 열에서 NULLS를 허용하면주기가 중단됩니다. 또는 우리는 EF 스키마 생성기를 속이고 있습니다.

내 경우에는이 간단한 수정으로 문제가 해결됩니다.


3
주의 독자. 스키마 정의 문제를 해결할 수는 있지만 의미를 변경합니다. 경기가 두 팀없이 이루어질 수있는 것은 아닐 것입니다.
N8allan 5

14

Cascade Deletes는 기본적으로 활성화되어 있기 때문입니다. 문제는 엔터티에서 삭제를 호출하면 각 f 키 참조 엔터티도 삭제한다는 것입니다. 이 문제를 해결하기 위해 '필수'값을 널 입력 가능으로 설정해서는 안됩니다. 더 좋은 옵션은 EF Code First의 Cascade 삭제 규칙을 제거하는 것입니다.

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); 

매핑 / 구성 할 때 각 자식에 대해 연속 삭제를 수행 할시기를 명시 적으로 표시하는 것이 더 안전 할 것입니다. 실체.


이것이 실행 된 후 무엇입니까? Restrict대신에 Cascade?
Jo Smo

4

InverseProperty EF Core는 솔루션을 쉽고 깨끗하게 만듭니다.

InverseProperty

따라서 원하는 해결책은 다음과 같습니다.

public class Team
{
    [Key]
    public int TeamId { get; set;} 
    public string Name { get; set; }

    [InverseProperty(nameof(Match.HomeTeam))]
    public ICollection<Match> HomeMatches{ get; set; }

    [InverseProperty(nameof(Match.GuestTeam))]
    public ICollection<Match> AwayMatches{ get; set; }
}


public class Match
{
    [Key]
    public int MatchId { get; set; }

    [ForeignKey(nameof(HomeTeam)), Column(Order = 0)]
    public int HomeTeamId { get; set; }
    [ForeignKey(nameof(GuestTeam)), Column(Order = 1)]
    public int GuestTeamId { get; set; }

    public float HomePoints { get; set; }
    public float GuestPoints { get; set; }
    public DateTime Date { get; set; }

    public Team HomeTeam { get; set; }
    public Team GuestTeam { get; set; }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.