one many framework first example dbmodelbuilder code c# entity-framework code-first ef-fluent-api

c# - first - one to many relationship entity framework fluent api



API fluida, muchos a muchos en Entity Framework Core 2.0 (3)

He buscado en stackoverflow una solución adecuada para generar una relación de muchos a muchos , utilizando EF Core 2.0, Code first y Fluent API.

Un escenario simple sería:

public class Person { public Person() { Clubs = new HashSet<Club>(); } public int PersonId { get; set; } public virtual ICollection<Club> Clubs { get; set; } } public class Club { public Club() { Persons = new HashSet<Person>(); } public int ClubId { get; set; } public virtual ICollection<Person> Persons { get; set; } }

Corríjame si estoy equivocado, pero honestamente no puedo encontrar una pregunta que contenga una explicación detallada sobre cómo hacerlo utilizando las herramientas descritas. ¿Alguien puede explicar cómo se hace esto?


Así que cada Person tiene cero o más Clubs y cada Club tiene cero o más Persons . Como dijo 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 de la estructura de la entidad es que reconoce esta relación y crea esta tabla adicional para usted.

A primera vista, parece un problema que esta tabla adicional no sea un dbSet en su DbContext : "¿Cómo realizar una combinación con esta tabla adicional si no tengo un DbSet para ella?".

Afortunadamente, no necesita mencionar esta tabla adicional en sus consultas.

Si necesita una consulta como "Dame todos los ''Clubs'' que ... de cada ''Persona'' que ..." no pienses en unirte. En su lugar, utilice las colecciones de IC!

Obtenga todas las personas de "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), };

Entity Framework reconocerá que se necesita una combinación con la tabla extra de muchos a muchos y realizará esta combinación sin que usted mencione esta tabla adicional.

Al revés: conseguir 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, encontré 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. Entity framework utilizará internamente las uniones adecuadas.


Esto aún no es posible en EF Core sin usar una clase explícita para la unión. Vea here un ejemplo de cómo hacer eso.

Hay un issue 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<PersonClub> PersonClubs { get; set; } } public class Club { public int ClubId { get; set; } public virtual ICollection<PersonClub> 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; } }

La siguiente OnModelCreating se usaría para la configuración:

protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<PersonClub>() .HasKey(pc => new { pc.PersonId, pc.ClubId }); modelBuilder.Entity<PersonClub>() .HasOne(pc => pc.Person) .WithMany(p => p.PersonClubs) .HasForeignKey(pc => pc.PersonId); modelBuilder.Entity<PersonClub>() .HasOne(pc => pc.Club) .WithMany(c => c.PersonClubs) .HasForeignKey(pc => pc.ClubId); }

Asegúrate de ir al tema abierto que vinculé y expresar tu frustración si sientes la necesidad.

EDITAR: El problema abierto sugiere usar un simple Select para navegar a través de esta jerarquía un tanto engorrosa. Para pasar de un PersonId a una colección de Club s, puede utilizar SelectMany . p.ej:

var clubs = dbContext.People .Where(p => p.PersonId == id) .SelectMany(p => p.PersonClubs); .Select(pc => pc.Club);

No puedo responder si esto es realmente una "mejor práctica", pero ciertamente debería hacer el truco 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<PersonClub> PersonClubs { get; set; } } public class Club { public int ClubId { get; set; } public virtual ICollection<PersonClub> 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<PersonClub>() .HasKey(pc => new { pc.PersonId, pc.ClubId }); }

Por lo tanto, este bloque para configurar la "tabla de pegamento" no es necesario como en el ejemplo de @Kirk:

modelBuilder.Entity<PersonClub>() .HasOne(pc => pc.Person) .WithMany(p => p.PersonClubs) .HasForeignKey(pc => pc.PersonId); modelBuilder.Entity<PersonClub>() .HasOne(pc => pc.Club) .WithMany(c => c.PersonClubs) .HasForeignKey(pc => pc.ClubId);