我使用数据库优先方法在ASP.NET MVC 5和Entity Framework 6的顶部使用c#编写了一个应用程序。
我有一个Student
模型,一个ClassRoom
模型和一个关系模型,用于将两个关系链接在一起,称为StudentToClassRoom
。
我希望能够选择所有学生,并希望每个学生都能获得学生关系中的所有ClassRoom
。
以下是我的模特
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; }
}
这是我试过的
var students = DbContext.Students.Include(x => x.ClassRoomRelations)
.ToList();
然而,这给了我每个学生的关系收集。但我希望能够获得每个学生的ClassRoom
信息。所以我想在Student和ClassRoom之间创建一个Has-Many-Through。在最终结果中,我并不关心ClassRoomRelations
,我只关心Student
和ClassRoom
对象。
如何使用Entity Framework获取每位学生的学生列表和所有课堂的集合?
答案 0 :(得分:1)
由于您已经公开了桥接表,您可以使用:
var studentRooms = DbContext.StudentToClassRoom
.Include(x => x.Student)
.Include(x => x.ClassRoom)
.ToList();
请参阅here
此外,您并不需要[Inverse]注释 - EF知道您正在使用FK链接到Id。
编辑:学生和他们的教室
首先,您需要修复学生模型:
public virtual ICollection<StudentToClassRoom> ClassRoomRelations { get; set; }
然后你可以运行
var studentAndRooms = DbContext.Students
.Select(s => new
{
student = s,
classrooms = s.ClassRoomRelations.Select(r => r.ClassRoom)
}).ToList();
答案 1 :(得分:1)
实体不支持此类关系。
但是,您可能会得到相同的结果(我没有测试过代码)
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();
您基本上选择了所有关系结果以及Student
和ClassRoom
模型。然后由学生将它们分组,让1名学生参加许多ClassRooms。
我希望这会有所帮助
答案 2 :(得分:0)
你为什么不简单地使用?您已经可以获得学生的课堂信息。
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()
答案 3 :(得分:0)
实体框架可以更好地处理多对多的关系。
EF的思考方式是学生有教室,ClassRoom有学生:
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; }
}
在EF地图中完全忽略关系表。看看this tutorial。
修改强>
下面是几个查询,以简化如何使用此地图场景:
获取特定学生的所有教室的列表:
var classRoomsOfSpecificStudent = DbContext
.Students
.First(s => s.Id == studentId)
.ClassRooms
.ToList();
获取名称中包含“a”的学生的所有教室的列表。
var classRooms = DbContext
.Students
.Where(s => s.Name.Contains("a"))
.SelectMany(s => s.ClassRooms)
.ToList();
让所有名字中包含“a”且课堂名称中包含“2b”的学生。
var students = DbContext
.Students
.Where(s => s.Name.Contains("a"))
.Where(s => s.ClassRooms.Any(c => c.Name.Contains("2b")))
.ToList();
我希望我澄清一点。
答案 4 :(得分:0)
如果要使用显式桥接表,则通常不应使用人工密钥。桥表上的外键列(StudentId,ClassRoomId)需要是一个键,因此有一个额外的键是无用的开销。
查询M2M关系看起来像这样:
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();
}
}
}
}