Saltar al contenido

API fluida, muchos a muchos en Entity Framework Core

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 ClubS, 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.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *