web-dev-qa-db-ja.com

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

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

239
Jarek

これを試して:

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によって参照される単一のコレクションを持つことはできません。これらの自己参照多対多では機能しないため、カスケード削除を使用せずに一致がマップされます。

277
Ladislav Mrnka

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

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

そうすれば、OnModelCreateメソッドにコードを追加する必要はありません

50
ShaneA

私はそれが数年前の投稿であることを知っており、上記の解決策で問題を解決することができます。ただし、まだ必要な人には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; }
}

InversePropertyの詳細については、MSDNをご覧ください: https://msdn.Microsoft.com/en-us/data/jj591583?f=255&MSPPError=-2147217396#Relationships

36
khoa_chung_89

あなたもこれを試すことができます:

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列でNULLを許可すると、サイクルが中断されます。または、EFスキーマジェネレーターをごまかしています。

私の場合、この簡単な変更で問題は解決します。

13
Maico

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

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); 

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

12
juls

EF CoreのInversePropertyは、ソリューションを簡単かつクリーンにします。

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; }
}
0
pritesh agrawal