wake-up-neo.com

Entity Framework Code First - zwei Fremdschlüssel aus derselben Tabelle

Ich habe gerade erst mit der Verwendung von EF-Code begonnen, daher bin ich ein absoluter Anfänger in diesem Thema.

Ich wollte Beziehungen zwischen Teams und Matches herstellen:

1 Spiel = 2 Mannschaften (Heim, Gast) und Ergebnis.

Ich dachte, es sei einfach, ein solches Modell zu erstellen, und begann mit dem Codieren:

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

Und ich bekomme eine Ausnahme:

Die referenzielle Beziehung führt zu einer zyklischen Referenz, die nicht zulässig ist. [Einschränkungsname = Match_GuestTeam]

Wie kann ich ein solches Modell mit 2 Fremdschlüsseln für dieselbe Tabelle erstellen?

244
Jarek

Versuche dies:

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

Primärschlüssel werden standardmäßig zugeordnet. Die Mannschaft muss zwei Streichhölzer haben. Es ist nicht möglich, dass eine einzelne Sammlung von zwei FKs referenziert wird. Übereinstimmungen werden ohne kaskadierendes Löschen zugeordnet, da dies bei diesen selbstreferenzierenden Viele-zu-Viele-Vorgängen nicht funktioniert.

281
Ladislav Mrnka

Es ist auch möglich, das ForeignKey() -Attribut in der Navigationseigenschaft anzugeben:

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

Auf diese Weise müssen Sie der Methode OnModelCreate keinen Code hinzufügen

51
ShaneA

Ich weiß, dass es sich um einen mehrere Jahre alten Beitrag handelt, und Sie können Ihr Problem mit der obigen Lösung lösen. Ich möchte jedoch nur vorschlagen, InverseProperty für jemanden zu verwenden, der es noch braucht. Zumindest müssen Sie in OnModelCreating nichts ändern.

Der folgende Code ist nicht getestet.

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

Weitere Informationen zu InverseProperty finden Sie unter MSDN: https://msdn.Microsoft.com/en-us/data/jj591583?f=255&MSPPError=-2147217396#Relationships

39
khoa_chung_89

Sie können dies auch versuchen:

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

Wenn Sie eine FK-Spalte auf NULL setzen, wird der Zyklus unterbrochen. Oder wir betrügen nur den EF-Schema-Generator.

In meinem Fall löst diese einfache Modifikation das Problem.

13
Maico

Dies liegt daran, dass Kaskadenlöschvorgänge standardmäßig aktiviert sind. Das Problem besteht darin, dass beim Aufrufen eines Löschvorgangs für die Entität auch alle Entitäten gelöscht werden, auf die mit dem f-Schlüssel verwiesen wird. Sie sollten die erforderlichen Werte nicht auf null setzen, um dieses Problem zu beheben. Eine bessere Option wäre, die EF Code First-Konvention zum Löschen von Kaskaden zu entfernen:

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); 

Es ist wahrscheinlich sicherer, beim Zuordnen/Konfigurieren explizit anzugeben, wann ein Kaskadenlöschvorgang für jedes der untergeordneten Elemente durchgeführt werden soll. das Unternehmen.

13
juls

InverseProperty in EF Core macht die Lösung einfach und sauber.

InverseProperty

Die gewünschte Lösung wäre also:

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