Fließende API, Viele-zu-Viele in Entity Framework Core 2.0

Ich habe stackoverflow für eine richtige Lösung zum Erstellen einer Viele-zu-viele- Beziehung gesucht, mit EF Core 2.0, Code zuerst und Fluent API.

Ein einfaches Szenario wäre:

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

Bitte korrigieren Sie mich, wenn ich falsch liege, aber ich könnte ehrlich gesagt keine Frage finden, die eine ausführliche Erklärung enthält, wie dies mit den beschriebenen Tools zu tun ist. Kann mir jemand erklären, wie das gemacht wird?

Dies ist in EF Core noch nicht möglich, ohne eine explizite class für den Join zu verwenden. Sehen Sie hier für ein Beispiel, wie man das macht.

Es gibt ein offenes Problem bei Github, das nach der Möglichkeit fragt, dies ohne eine explizite class zu tun, aber es ist noch nicht abgeschlossen.

Unter Verwendung Ihres Szenarios würde das Beispiel, das ich verlinkt habe, die folgenden Entitätsklassen empfehlen:

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

Das folgende OnModelCreating dann für die Einrichtung verwendet:

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

Seien Sie sicher, zu der offenen Frage zu gehen, die ich verknüpfte, und Ihre Frustration auszusprechen, wenn Sie die Notwendigkeit fühlen.

BEARBEITEN: Das offene Problem schlägt vor, eine einfache Select zu verwenden, um durch diese etwas schwerfällige Hierarchie zu navigieren. Um von einer PersonId zu einer Sammlung von Club , können Sie SelectMany . z.B:

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

Ich kann nicht dafür bürgen, ob dies wirklich eine “Best Practice” ist, aber es sollte sicherlich den Trick machen und ich denke, es ist fair zu sagen, dass es nicht übermäßig hässlich ist.

Das richtige “Setup” dafür ist:

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

Daher ist dieser Block zum Konfigurieren der “Leimtabelle” nicht notwendig, wie in @Kirk Beispiel:

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

Jede Person hat also null oder mehr Clubs und jeder Club hat null oder mehr Persons . Wie Sie richtig gesagt haben, ist dies eine richtige Viele-zu-Viele-Beziehung.

Sie wissen wahrscheinlich, dass eine relationale database eine zusätzliche Tabelle benötigt, um diese Viele-zu-Viele-Beziehung zu implementieren. Das Schöne am Entity-Framework ist, dass es diese Beziehung erkennt und diese zusätzliche Tabelle für Sie erstellt.

Auf den ersten Blick scheint es ein Problem zu sein, dass diese zusätzliche Tabelle kein dbSet in Ihrem DbContext : “Wie DbContext ich einen Join mit dieser zusätzlichen Tabelle durch, wenn ich kein DbSet dafür habe?”.

Glücklicherweise müssen Sie diese zusätzliche Tabelle in Ihren Abfragen nicht erwähnen.

Wenn Sie eine Frage wie “Geben Sie mir alle Clubs”, die … von jeder ‘Person’, die … “nicht in Joins denken. Verwende stattdessen die ICollections!

Erhalten Sie alle “John Doe” Personen mit allen Country Clubs, an denen sie teilnehmen:

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

Das Entitätsframework erkennt, dass ein Join mit der zusätzlichen Viele-zu-Viele-Tabelle erforderlich ist, und führt diesen Join durch, ohne dass Sie diese zusätzliche Tabelle erwähnen.

Umgekehrt: Hol dir alle Country Clubs mit ihren “John Doe” Personen:

 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"), } 

Ich habe erlebt, dass ich, sobald ich begonnen habe, in den resultierenden Sammlungen zu denken, die ich anstelle der Joins, die ich brauchte, um diese Sammlungen zu bekommen, fand, dass ich die Joins kaum verwende. Dies gilt sowohl für Eins-zu-Viele-Beziehungen als auch für Viele-zu-Viele-Beziehungen. Entity Framework verwendet intern die richtigen Joins.