1

J'ai une application écrite en utilisant C# sur le dessus de ASP.NET MVC 5 et Entity Framework 6 en utilisant l'approche Database-First.Comment puis-je créer une relation Has-Many-Through avec Entity Framework 6?

J'ai un modèle Student, un modèle ClassRoom et un modèle relationnel pour relier les deux relations ensemble appelé StudentToClassRoom. Je veux être en mesure de sélectionner tous les étudiants et pour chaque étudiant je veux obtenir tous les ClassRoom que l'élève a aussi une relation.

Voici mes modèles

public class Student 
{ 
    [Key] 
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public int Id { get; set; } 
    public string Name { get; set; } 

    public virtual ICollection<ClassRoom> ClassRoomRelations { get; set; } 
} 


public class StudentToClassRoom 
{ 
    [Key] 
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public int Id { get; set; } 

    [ForeignKey("Student")] 
    [InverseProperty("Id")] 
    public int StudentId { get; set; } 

    [ForeignKey("ClassRoom")] 
    [InverseProperty("Id")] 
    public int ClassRoomId { get; set; } 

    public virtual Student Student { get; set; } 

    public virtual ClassRoom ClassRoom { get; set; } 
} 

public class ClassRoom 
{ 
    [Key] 
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

Voici ce que j'ai essayé

var students = DbContext.Students.Include(x => x.ClassRoomRelations) 
           .ToList(); 

Cependant, cela me donne la collection de relation pour chaque élève. Mais je veux être en mesure d'obtenir l'information ClassRoom pour chaque élève. Donc je veux créer un Has-Many-Through entre Student et ClassRoom. Dans les résultats finaux, je ne me soucie pas vraiment du ClassRoomRelations, je me soucie seulement de l'Student et des objets ClassRoom.

Comment puis-je obtenir une liste de Student et une collection de toutes les classes pour chaque étudiant utilisant Entity Framework?

+0

Ne perdez pas votre temps à chercher. EF ne supporte pas une telle relation. –

+0

@IvanStoev merci :) Y at-il une solution de contournement pour obtenir le même jeu de données? –

+0

Seulement si vous avez réellement besoin de 'many-to-many' avec une table de jonction implicite. Même si cela est problématique si vous êtes lié à la base de données existante ('StudentToClassRoom' n'est pas éligible pour la table de jonction automatique). –

Répondre

1

entité ne prend pas en charge ce type de relation.

Mais, vous pourriez être en mesure d'obtenir le même résultat que si (je ne l'ai pas testé le code)

var studentRooms = DbContext.StudentToClassRoom 
          .Include(x => x.Student) 
          .Include(x => x.ClassRoom) 
          .GroupBy(x => x.Student) 
          .Select(x => new { 
           Student => x.Key 
           ClassRooms => x.Select(relation => relation.ClassRoom) 
          }) 
          .ToList(); 

Vous sélectionnez essentiellement tout le résultat de la relation avec les modèles Student et ClassRoom. Ensuite, vous les regroupez par l'étudiant pour obtenir 1 étudiant à plusieurs classes.

J'espère que cela aide

+0

Merci beaucoup.Cela marche! –

0

Pourquoi n'utilisez-vous pas simplement? Vous pouvez déjà obtenir des informations sur la classe de l'élève.

public class Student 
{ 
    public int Id { get; set; } 

    public string Name { get; set; } 

    public Guid ClassRoomId { get; set; } 

    // public virtual ClassRoom ClassRoom { get; set; } 
} 

public class ClassRoom 
{ 
    public int Id { get; set; } 

    public string Name { get; set; } 

    // public virtual ICollection<Student> Students{ get; set; } 
} 

public class StudentToClassRoom 
{ 
    public int Id { get; set; } 

    public Guid StudentId { get; set; } 

    public virtual Student Student { get; set; } 

    public Guid ClassRoomId { get; set; } 

    public virtual ClassRoom ClassRoom { get; set; } 
} 

// var students = DbContext.Students.Include(x => x.ClassRoom).ToList(); 

var mergedRecords = DbContext.StudentToClassRoom 
          .Include(x => x.Student) 
          .Include(x => x.ClassRoom) 
          .ToList() 
+0

Mon 'ClassRoom' n'a pas de propriété/colonne' StudentId', donc je peut créer une propriété de navigation directement de Student vers 'ClassRoom'. Le modèle 'StudentToClassRoom' est nécessaire pour pouvoir identifier la relation entre les deux. –

+0

J'ai modifié mon message. Vérifiez à nouveau s'il vous plaît. –

0

Entity Framework a une meilleure approche pour gérer de nombreuses relations.

chemin de l'EF penser que c'est l'élève a des salles de classe et a Classroom étudiants:

public class Student 
{ 
    [Key] 
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public int Id { get; set; } 
    public string Name { get; set; } 

    public virtual ICollection<ClassRoom> ClassRooms { get; set; } 
} 

public class ClassRoom 
{ 
    [Key] 
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public int Id { get; set; } 
    public string Name { get; set; } 

    public virtual ICollection<Student> Students { get; set; } 
} 

Le tableau de la relation est complètement ignorée dans la carte EF. Jetez un coup d'oeil dans this tutorial.

EDIT:
Voici quelques requêtes pour simplifier la façon d'utiliser ce scénario de carte:

obtenir une liste de toutes les salles de classe d'un étudiant spécifique:

var classRoomsOfSpecificStudent = DbContext 
    .Students 
    .First(s => s.Id == studentId) 
    .ClassRooms 
    .ToList(); 

Obtenir une liste de toutes les salles de classe des élèves dont le nom contient «a». Obtenez tous les étudiants dont le nom contient «a» et le nom des salles de classe contenant «2b».

var students = DbContext 
    .Students 
    .Where(s => s.Name.Contains("a")) 
    .Where(s => s.ClassRooms.Any(c => c.Name.Contains("2b"))) 
    .ToList(); 

J'espère avoir clarifié un peu.

+0

Je suis confus. Mon 'ClassRoom' n'a pas de propriété/colonne' StudentId' donc je peux créer une propriété de navigation de 'Student' directement à' ClassRoom'. Le modèle 'StudentToClassRoom' est nécessaire pour pouvoir identifier la relation entre les deux. –

+0

J'ai édité la réponse avec quelques questions, je pense clarifiera un peu sur la façon d'utiliser cette carte. –

1

Puisque vous avez exposé la table de bridge, vous pouvez aller avec:

var studentRooms = DbContext.StudentToClassRoom 
          .Include(x => x.Student) 
          .Include(x => x.ClassRoom) 
          .ToList(); 

Voir here

En outre, vous n'avez pas vraiment besoin [inverse] annotations - EF sait que vous liez Id avec le FK.

EDIT: Un étudiant et leurs salles de classe

D'abord, vous aurez besoin de fixer votre modèle étudiant:

public virtual ICollection<StudentToClassRoom> ClassRoomRelations { get; set; } 

vous pouvez exécuter

var studentAndRooms = DbContext.Students 
         .Select(s => new 
         { 
         student = s, 
         classrooms = s.ClassRoomRelations.Select(r => r.ClassRoom) 
         }).ToList(); 
+0

C'est un bon début, mais y a-t-il un moyen de sélectionner un étudiant et plusieurs salles de classe afin que les résultats finaux accèdent à student.ClassRooms? –

+0

Voir modification. En outre, votre modèle a besoin du correctif. –

+0

Merci beaucoup pour votre aide. Je pense que la réponse @Jaylen est très similaire à cette approche, mais a fonctionné. –

0

Si vous souhaitez utiliser un explicite table de pont, il ne devrait généralement pas avoir une clé artificielle. Les colonnes de clé étrangère (StudentId, ClassRoomId) sur la table de pont doivent être une clé, et donc avoir une clé supplémentaire est inutile.

Et la relation entre l'interrogation M2M ressemble à ceci:

using System; 
using System.Collections.Generic; 
using System.ComponentModel.DataAnnotations; 
using System.ComponentModel.DataAnnotations.Schema; 
using System.Data.Entity; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

namespace Ef6Test 
{ 

    public class Student 
    { 
     [Key] 
     [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
     public int Id { get; set; } 
     public string Name { get; set; } 

     public virtual ICollection<StudentToClassRoom> StudentToClassRoom { get; set; } = new HashSet<StudentToClassRoom>(); 
    } 


    public class StudentToClassRoom 
    { 

     [ForeignKey("Student"), Column(Order = 0), Key()] 
     public int StudentId { get; set; } 

     [ForeignKey("ClassRoom"), Column(Order = 1), Key()] 
     public int ClassRoomId { get; set; } 

     public virtual Student Student { get; set; } 

     public virtual ClassRoom ClassRoom { get; set; } 
    } 

    public class ClassRoom 
    { 
     [Key] 
     [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
     public int Id { get; set; } 
     public string Name { get; set; } 
    } 

    class Db: DbContext 
    { 

     public DbSet<Student> Students { get; set; } 
     public DbSet<ClassRoom> Classrooms { get; set; } 

     public DbSet<StudentToClassRoom> StudentToClassRoom { get; set; } 

    } 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Database.SetInitializer(new DropCreateDatabaseIfModelChanges<Db>()); 

      using (var db = new Db()) 

      { 

       var students = Enumerable.Range(1, 150).Select(i => new Student() { Name = $"Student{i}" }).ToList(); 
       var classRooms = Enumerable.Range(1, 20).Select(i => new ClassRoom() { Name = $"ClassRoom{i}" }).ToList(); 

       var rand = new Random(); 
       foreach(var s in students) 
       { 
        var classRoomId = rand.Next(0, classRooms.Count - 10); 
        s.StudentToClassRoom.Add(new StudentToClassRoom() { Student = s, ClassRoom = classRooms[classRoomId] }); 
        s.StudentToClassRoom.Add(new StudentToClassRoom() { Student = s, ClassRoom = classRooms[classRoomId+1] }); 
        s.StudentToClassRoom.Add(new StudentToClassRoom() { Student = s, ClassRoom = classRooms[classRoomId+2] }); 

       } 

       db.Students.AddRange(students); 
       db.Classrooms.AddRange(classRooms); 
       db.SaveChanges(); 

      } 
      using (var db = new Db()) 
      { 
       db.Configuration.LazyLoadingEnabled = false; 
       var q = db.Students.Include("StudentToClassRoom.ClassRoom"); 

       var results = q.ToList(); 
       Console.WriteLine(q.ToString()); 


       Console.ReadKey(); 
      } 

     } 
    } 
}