Entity Framework Code First-同じテーブルからの2つの外部キー


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つの外部キーを使用して、このようなモデルを作成するにはどうすればよいですか?

回答:


297

これを試して:

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

主キーはデフォルトの規則でマップされます。チームには2つのマッチのコレクションが必要です。2つのFKによって参照される単一のコレクションを持つことはできません。マッチは、これらの自己参照多対多では機能しないため、カスケード削除なしでマップされます。


3
2つのチームが1度しかプレイできない場合はどうなりますか?
ca9163d9 2013年

4
@NickW:これは、マッピングではなく、アプリケーションで処理する必要があるものです。マッピングの観点から、ペアは2回プレイすることが許可されています(それぞれゲストとホームです)。
Ladislav Mrnka 2013年

2
私は似たようなモデルを持っています。チームが削除された場合にカスケード削除を処理する適切な方法は何ですか?INSTEAD OF DELETEトリガーの作成を検討しましたが、より良い解決策があるかどうかわかりませんか?アプリケーションではなく、DBでこれを処理したいのですが。
ウッドチッパー2014

1
@mrshickadance:同じです。1つのアプローチは、流れるようなAPIと別のデータ注釈を使用します。
Ladislav Mrnka、2014

1
WillCascadeOnDelete falseを使用すると、チームを削除したい場合にエラーがスローされます。「Team_HomeMatches」AssociationSetからの関係は「削除済み」状態です。多重度の制約がある場合、対応する「Team_HomeMatches_Target」も「削除済み」状態である必要があります。
Rupesh Kumar Tiwari 2015年

55

ForeignKey()ナビゲーションプロパティで属性を指定することもできます。

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

これにより、OnModelCreateメソッドにコードを追加する必要がなくなります。


4
どちらの方法でも同じ例外が発生します。
Jo Smo、2015

11
これは、エンティティに同じタイプの複数のnavプロパティが含まれている場合(HomeTeamおよびGuestTeamのシナリオと同様)を除き、すべてのケースで機能する外部キーを指定する私の標準的な方法です。この場合、EFはSQLの生成で混乱します。解決策はOnModelCreate、受け入れられた回答と関係の両側の2つのコレクションに従ってコードを追加することです。
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
この回答に感謝しますが、一致テーブルの外部キー列をnullにできます。
RobHurd

これは、null許容コレクションが必要なEF 6で私にとって非常に役立ちました。
Pynt

流暢なAPIを避けたい場合(何らかの理由で#differentdiscussion)、これは素晴らしい働きをします。私の場合、フィールド/テーブルにPKの文字列があるため、「Match」エンティティに追加のforiegnKeyアノテーションを含める必要がありました。
DiscipleMichael

1
これは私にとって非常にうまくいきました。ところで 列をヌル可能にしたくない場合は、[ForeignKey]属性で外部キーを指定できます。キーがnullにできない場合は、これで準備は完了です。
Jakub Holovsky、2018

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
注意読者。これによりスキーマ定義の問題を回避できる可能性がありますが、セマンティクスが変更されます。おそらく、2つのチームがなくても試合ができるわけではありません。
N8allan

14

これは、カスケード削除がデフォルトで有効になっているためです。問題は、エンティティに対して削除を呼び出すと、fキーが参照するエンティティもそれぞれ削除されることです。この問題を解決するために、「必須」の値をヌル可能にしないでください。より良いオプションは、EF Code Firstのカスケード削除規則を削除することです。

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); 

マッピング/設定時に、子のそれぞれに対してカスケード削除をいつ行うかを明示的に示す方がおそらく安全です。エンティティ。


これが実行された後、それは何ですか?Restrictの代わりにCascade
Jo Smo、2015

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.