Entity Framework Code First - dos Foreign Keys de la misma tabla

Acabo de empezar a utilizar el código EF, así que soy un principiante total en este tema.

Quería crear relaciones entre Equipos y Partidos:

1 partido = 2 equipos (local, invitado) y resultado.

Pensé que era fácil crear un modelo de este tipo, así que empecé a programar:

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

Y me sale una excepción:

La relación referencial resultará en una referencia cíclica que no está permitida. [ Nombre de restricción = Match_GuestTeam ]

¿Cómo puedo crear un modelo de este tipo, con 2 claves externas a la misma tabla?

Solución

Prueba esto:

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

    public virtual ICollection HomeMatches { get; set; }
    public virtual ICollection 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()
                    .HasRequired(m => m.HomeTeam)
                    .WithMany(t => t.HomeMatches)
                    .HasForeignKey(m => m.HomeTeamId)
                    .WillCascadeOnDelete(false);

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

Las claves primarias se asignan por convención predeterminada. El equipo debe tener dos colecciones de partidos. No se puede tener una sola colección referenciada por dos FKs. Las coincidencias se asignan sin eliminación en cascada porque no funciona en estos muchos-a-muchos auto-referenciados.

Comentarios (10)

También es posible especificar el atributo ForeignKey() en la propiedad de navegación:

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

De esta forma no es necesario añadir código al método `OnModelCreate

Comentarios (3)

También puedes probar esto:

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

Cuando haces que una columna FK permita NULLS, estás rompiendo el ciclo. O simplemente estamos engañando al generador de esquemas EF.

En mi caso, esta simple modificación resuelve el problema.

Comentarios (1)