Código de marco de entidad primero: dos claves externas de la misma tabla

Resuelto Jarek asked hace 13 años • 7 respuestas

Acabo de empezar a usar el código EF, por lo 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 sería fácil crear un modelo así, así que comencé a codificar:

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 dará lugar a 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 para la misma tabla?

Jarek avatar Apr 06 '11 04:04 Jarek
Aceptado

Prueba esto:

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

Las claves primarias se asignan por convención predeterminada. El equipo debe tener dos colecciones de partidos. No puede tener una sola colección a la que hagan referencia dos FK. La coincidencia se asigna sin eliminación en cascada porque no funciona en estas autorreferencias de muchos a muchos.

Ladislav Mrnka avatar Apr 05 '2011 22:04 Ladislav Mrnka

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

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

De esa manera no necesitas agregar ningún código al OnModelCreatemétodo.

ShaneA avatar Jan 16 '2013 19:01 ShaneA

Puedes probar esto también:

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 soluciona el problema.

Maico avatar Jan 09 '2012 23:01 Maico

InversePropertyen EF Core hace que la solución sea fácil y limpia.

Propiedad inversa

Entonces la solución deseada sería:

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; }
}
pritesh agrawal avatar Aug 13 '2019 03:08 pritesh agrawal

Esto se debe a que las eliminaciones en cascada están habilitadas de forma predeterminada. El problema es que cuando realiza una eliminación en la entidad, también eliminará cada una de las entidades a las que se hace referencia con la tecla F. No debe hacer que los valores "requeridos" sean anulables para solucionar este problema. Una mejor opción sería eliminar la convención de eliminación en cascada de EF Code First:

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); 

Probablemente sea más seguro indicar explícitamente cuándo realizar una eliminación en cascada para cada uno de los elementos secundarios al mapear/configurar. la entidad.

juls avatar Nov 20 '2013 10:11 juls