Nuestro equipo de trabajo ha estado mucho tiempo investigando soluciones a tu duda, te ofrecemos la respuestas de modo que esperamos servirte de mucha apoyo.
Solución:
EF Núcleo 5.0 RC1+
A partir de EF Core 5.0 RC1, es posible hacer esto sin una tabla de combinación explícita. EF Core puede configurar una asignación para la relación de muchos a muchos que se muestra en su pregunta sin necesidad de que cree un PersonClub
tipo.
Consulte Novedades en EF Core 5.0, RC1, Many-to-many en los documentos oficiales para obtener más información.
Versión anterior
Esto aún no es posible en EF Core sin usar una clase explícita para la unión. Vea aquí un ejemplo de cómo hacerlo.
Hay un problema abierto en Github que solicita la capacidad de hacer esto sin la necesidad de una clase explícita, pero aún no se ha completado.
Usando su escenario, el ejemplo que vinculé recomendaría las siguientes clases de entidad:
public class Person
public int PersonId get; set;
public virtual ICollection PersonClubs get; set;
public class Club
public int ClubId get; set;
public virtual ICollection PersonClubs get; set;
public class PersonClub
public int PersonId get; set;
public Person Person get; set;
public int ClubId get; set;
public Club Club get; set;
El seguimiento OnModelCreating
luego se usaría para la configuración:
protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.Entity()
.HasKey(pc => new pc.PersonId, pc.ClubId );
modelBuilder.Entity()
.HasOne(pc => pc.Person)
.WithMany(p => p.PersonClubs)
.HasForeignKey(pc => pc.PersonId);
modelBuilder.Entity()
.HasOne(pc => pc.Club)
.WithMany(c => c.PersonClubs)
.HasForeignKey(pc => pc.ClubId);
Asegúrese de ir al problema abierto que vinculé y exprese su frustración si siente la necesidad.
EDITAR: El problema abierto sugiere usar un simple Select
para navegar a través de esta jerarquía algo engorrosa. Para obtener de un PersonId
a una colección de Club
S, puedes usar SelectMany
. p.ej:
var clubs = dbContext.People
.Where(p => p.PersonId == id)
.SelectMany(p => p.PersonClubs);
.Select(pc => pc.Club);
No puedo garantizar si esto es realmente una “mejor práctica”, pero sin duda debería funcionar y creo que es justo decir que no es demasiado feo.
La “configuración” correcta para esto es:
public class Person
public int PersonId get; set;
public virtual ICollection PersonClubs get; set;
public class Club
public int ClubId get; set;
public virtual ICollection PersonClubs get; set;
public class PersonClub
public int PersonId get; set;
public Person Person get; set;
public int ClubId get; set;
public Club Club get; set;
protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.Entity()
.HasKey(pc => new pc.PersonId, pc.ClubId );
Entonces, este bloque para configurar la “tabla de pegamento” es no necesario como en el ejemplo de @Kirk:
modelBuilder.Entity()
.HasOne(pc => pc.Person)
.WithMany(p => p.PersonClubs)
.HasForeignKey(pc => pc.PersonId);
modelBuilder.Entity()
.HasOne(pc => pc.Club)
.WithMany(c => c.PersonClubs)
.HasForeignKey(pc => pc.ClubId);
Así que cada Person
tiene cero o más Clubs
y cada Club
tiene cero o más Persons
. Como dijiste correctamente, esta es una relación adecuada de muchos a muchos.
Probablemente sepa que una base de datos relacional necesita una tabla adicional para implementar esta relación de muchos a muchos. Lo bueno del marco de la entidad es que reconoce esta relación y crea esta tabla adicional para usted.
A primera vista parece un problema que esta mesa extra no sea un dbSet
en tus DbContext
: “Cómo realizar una combinación con esta tabla adicional si no tengo una DbSet
¿para ello?”.
Afortunadamente, no es necesario que mencione esta tabla adicional en sus consultas.
Si necesita una consulta como “Dame todos los ‘Clubes’ que … de cada ‘Persona’ que …” no pienses en combinaciones. ¡En su lugar, use las ICollections!
Obtenga todas las personas “John Doe” con todos los clubes de campo a los que asisten:
var result = myDbContext.Persons
.Where(person => person.Name == "John Doe")
.Select(person => new
PersonId = person.Id,
PersonName = person.Name,
AttendedCountryClubs = person.Clubs
.Where(club => club.Type = ClubType.CountryClub),
;
El marco de la entidad reconocerá que se necesita una unión con la tabla adicional de muchos a muchos y realizará esta unión, sin que usted mencione esta tabla adicional.
Al revés: Obtener todos los clubes de campo con sus Personas “John Doe”:
var result = myDbContext.Clubs
.Where(club => club.Type = ClubType.CountryClub)
.Select(club => new
ClubId = club.Id,
ClubName = club.Name,
AnonymousMembers = club.Persons
.Where(person => person.Name == "John Doe"),
Experimenté que una vez que comencé a pensar en las colecciones resultantes que quiero en lugar de las uniones que necesitaba para obtener estas colecciones, descubrí que casi no uso las uniones. Este es el caso de las relaciones de uno a muchos, así como de las relaciones de muchos a muchos. El marco de la entidad utilizará internamente las uniones adecuadas.
Finalizando este artículo puedes encontrar las referencias de otros sys admins, tú también puedes insertar el tuyo si te apetece.