我有桌老师和桌上学生,对于表格中的每个记录'老师'表格中将有多个记录“学生”。这是我的模型类
public class Test
{
public int Id { get; set; }
public string Text { get; set; }
public List<StudentsDTO> Students{ get; set; }
}
这是我的linq查询我试图获取记录
var st = (from tt in context.Teachers
join ss in context.Students
on tt.ID equals ss.teacherID
where tt.TypeID == 2
select new Test
{
Id = tt.ID,
Text = tt.Text,
Students= new List<StudentsDTO>()
{
new StudentsDTO()
{
Name= ss.Name,
Id= ss.StudentID
}
}.ToList()
}).ToList();
return st;
我无法为教师表中的每条记录收集学生,怎么做?
答案 0 :(得分:5)
如果您需要左连接,请将in ssj
替换为in ssj.DefaultIfEmpty()
。
var st = (from tt in context.Teachers
where tt.TypeID == 2
join ss in context.Students
on tt.ID equals ss.teacherID into ssj
select new Test {
Id = tt.ID,
Text = tt.Text,
Students = (from ss in ssj
select new StudentsDTO() {
Name = ss.Name,
Id = ss.StudentID
}).ToList()
}).ToList();
return st;
这在LINQ中使用所谓的组连接 - 查询将每个tt与ssj中的ss集合(即{tt,group of ss})进行匹配。
答案 1 :(得分:5)
如果您对一对多关系使用了正确的实体框架类定义,那么您的查询将更加简单。你甚至不必使用连接,因为实体框架会为你做这件事。
请参阅Entity Framework Configure One-to-Many Relationship
如果一位教师拥有零个或多个学生,并且每个学生只有一位教师,那么一对多的建模如下:
class Teacher
{
public int Id {get; set;}
// a Teacher has zero or more Students:
public virtual ICollection<Student> Students {get; set;}
...
}
class Student
{
public int Id {get; set;}
// a Student has exactly one Teacher, via foreign key TeacherId
public int TeacherId {get; set;}
public virtual Teacher Teacher {get; set;}
...
}
class MyDbContext : DbContext
{
public DbSet<Teacher> Teachers {get; set;}
public DbSet<Student> Students {get; set;}
}
因为使用了正确的Code First Conventions,所以只需要模拟教师和学生之间的一对多关系。
在实体框架中,表的列由非虚拟属性表示。虚拟属性表示表之间的关系(一对多,多对多,......)
如果您需要不同的表名或列名,则必须使用属性或流畅的API,但ID仍然相同:教师对其拥有的许多学生进行ICollection,并且学生有一个外国钥匙和一个教师的财产。
正确定义模型后,您的查询更简单,更直观:
var result = myDbContext.Teachers // from the set of Teachers
.Where(teacher => teacher.TypeId == 2) // take all teachers with TypeId 2
.Select(teacher => new Test // from every remaining Teacher,
{ // create one Test object
Id = teacher.Id, // With Id is Teach Id
Text = teacher.Text, // Text is teacher.Text
Students = teacher.Students
.Select(student => new StudentDTO // from every Student of this Teacher
{ // create a StudentDTO object
Id = student.ID, // with Id = student.Id
Name= student.Name, // Name is student.Name
})
.ToList(), // create a list of these StudentDTOs
})
.ToList(); // create a list of all Test objects
我的经验是,由于我正确地为所有实体框架类建模,因此我很少再创建连接。我通常认为在集合中而不是连接表。实体框架将知道必须执行哪个(组)连接。
例如:如果您希望教师的所有学生的姓名都具有某个TeacherCode:
IEnumerable<Student> GetStudentsOfTeacher(string teacherCode)
{
return myDbContext.Students
.Where(student => student.Teacher.TeacherCode == teacherCode);
}
实体框架将为您加入TeacherId上的学生和教师。
如果你想进行小组加入(一个包含所有子项目的项目,一个包含所有学生的教师),请从单侧开始。如果你想做一个平坦的加入(一个有这个老师的学生)从多方开始。
除了更简单的linq查询之外,正确的建模还隐藏了数据库建模的方式。如果内部模型发生变化,dbContext的用户将不必更改其代码。
例如,如果您将师生关系更改为多对多关系,这意味着教师可能有很多学生,而学生可能有很多教师,那么您的教师课程的定义不会改变,因此只要课程没有改变,上面的查询就不会改变。