c# - relacionar - Código de Entity Framework primero: dos claves externas de la misma tabla
migraciones de code first (5)
Primero comencé a usar 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 el resultado. Pensé que es 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á como resultado una referencia cíclica que no está permitida. [Nombre de restricción = Match_GuestTeam]
¿Cómo puedo crear dicho modelo, con 2 claves externas para la misma tabla? TIA.
Esto se debe a que las eliminaciones en cascada están habilitadas de forma predeterminada. El problema es que cuando llama a una eliminación en la entidad, también eliminará cada una de las entidades referenciadas con la tecla f. No debe hacer que los valores ''requeridos'' sean anulables para solucionar este problema. Una opción mejor sería eliminar la convención de eliminación de Cascade de EF Code First:
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
Probablemente sea más seguro indicar explícitamente cuándo hacer una eliminación en cascada para cada uno de los hijos cuando se asigna / config. la entidad.
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 colección única referenciada por dos FK. La coincidencia se asigna sin eliminación en cascada porque no funciona en estas autorreferencia muchos a muchos.
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 creas una columna FK permite 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.
Sé que es una publicación de varios años y puede resolver su problema con la solución anterior. Sin embargo, solo quiero sugerir el uso de InverseProperty para alguien que aún lo necesita. Al menos no necesita cambiar nada en OnModelCreating.
El código de abajo no está probado.
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; }
}
Puede leer más sobre InverseProperty en MSDN: https://msdn.microsoft.com/en-us/data/jj591583?f=255&MSPPError=-2147217396#Relationships
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 esa manera, no es necesario agregar ningún código al método OnModelCreate