通用类和字段
定义老师类和学生类,学生类中有老师的ID
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int TeacherID { get; set; }
}
public class Teacher
{
public int Id { get; set; }
public string Name { get; set; }
}
定义一个连接后的数据类
public class StuTea
{
public int StuId { get; set; }
public string StuName { get; set; }
public int TeaId { get; set; }
public string TeaName { get; set; }
public override string ToString()
{
return string.Format("学生id:{0},学生name:{1},老师id:{2},老师name:{3}",StuId,StuName,TeaId,TeaName);
}
}
public class StuTeaG
{
public Student Stu { get; set; }
public Teacher Tea { get; set; }
public override string ToString()
{
return string.Format("学生id:{0},学生name:{1},老师id:{2},老师name:{3}",
Stu==null?-1: Stu.Id,Stu==null?"": Stu.Name, Tea == null?-1: Tea.Id, Tea == null?"": Tea.Name);
}
}
初始化学生数据和老师数据
public static List<Student> stus = new List<Student>()
{
new Student(){Id=1,Name="张三",TeacherID=1},
new Student(){Id=2,Name="李四",TeacherID=1},
new Student(){Id=3,Name="王五",TeacherID=2},
new Student(){Id=4,Name="赵六",TeacherID=3},
};
public static List<Teacher> teas = new List<Teacher>()
{
new Teacher(){Id=1,Name="刘老师"},
new Teacher(){Id=2,Name="徐老师"},
new Teacher(){Id=4,Name="杨老师"},
};
内连接
join字句
//内连接
var stuTeas = from stu in stus
join tea in teas on stu.TeacherID equals tea.Id
select new StuTea
{
StuId = stu.Id,
StuName = stu.Name,
TeaId = tea.Id,
TeaName = tea.Name
};
foreach (var stuTea in stuTeas)
{
Console.WriteLine(stuTea);
}
join方法
//使用join方法
var stuTeas = stus.Join(teas, stu => stu.TeacherID, tea => tea.Id, (stu, tea) => new StuTea
{
StuId = stu.Id,
StuName = stu.Name,
TeaId = tea.Id,
TeaName = tea.Name
});
foreach (var stuTea in stuTeas)
{
Console.WriteLine(stuTea);
}
运行结果:
学生id:1,学生name:张三,老师id:1,老师name:刘老师
学生id:2,学生name:李四,老师id:1,老师name:刘老师
学生id:3,学生name:王五,老师id:2,老师name:徐老师
内连接会取两个对象的交集,由于赵六的teacherId是3,找不到对应的老师。所以最后查询的结果中没有这个学生
分组连接
含有 into 表达式的 join 子句称为分组联接。分组联接会生成分层的结果序列,该序列将左侧源序列中的元素与右侧源序列中的一个或多个匹配元素相关联。
join子句
var stuTeas = from stu in stus
join tea in teas on stu.TeacherID equals tea.Id into gs
select new
{
Stu = stu,
Teas = gs
};
foreach (var stuTea in stuTeas)
{
Console.WriteLine(stuTea.Stu.Id+stuTea.Stu.Name);
foreach (var tea in stuTea.Teas)
{
Console.WriteLine(" "+tea.Id+tea.Name);
}
}
GroupJoin方法
var stuTeas = stus.GroupJoin(teas, stu => stu.TeacherID, tea => tea.Id,
(stu, teas) => new
{
StuId = stu.Id,
StuName = stu.Name,
teas = teas,
});
foreach (var item in stuTeas)
{
Console.WriteLine(item.StuId + item.StuName);
foreach (var tea in item.teas)
{
Console.WriteLine(" " + tea.Id + tea.Name);
}
}
运行结果:
1张三
1刘老师
2李四
1刘老师
3王五
2徐老师
4赵六
左外连接
在左外部联接中,将返回左侧源序列中的所有元素,即使右侧序列中没有其匹配元素也是如此。 若要在 LINQ 中执行左外部联接,请结合使用 DefaultIfEmpty 方法与分组联接,指定要在某个左侧元素不具有匹配元素时生成的默认右侧元素。不使用DefaultIfEmpty会和内联效果相同
以学生查询
代码stu
var stuTeas = from stu in stus
join tea in teas on stu.TeacherID equals tea.Id into gs
from g in gs.DefaultIfEmpty()
select new StuTea
{
StuId = stu.Id,
StuName = stu.Name,
TeaId=g==null?-1:g.Id,
TeaName= g == null ? "" : g.Name
};
foreach (var stuTea in stuTeas)
{
Console.WriteLine(stuTea);
}
运行结果
学生id:1,学生name:张三,老师id:1,老师name:刘老师
学生id:2,学生name:李四,老师id:1,老师name:刘老师
学生id:3,学生name:王五,老师id:2,老师name:徐老师
学生id:4,学生name:赵六,老师id:-1,老师name:
解析外联代码1
代码stu2
var stuTeas = from stu in stus
join tea in teas on stu.TeacherID equals tea.Id into gs
from g in gs.DefaultIfEmpty()
select new StuTeaG
{
Stu = stu,
Tea = g
};
foreach (var stuTea in stuTeas)
{
Console.WriteLine(stuTea);
}
代码stu1和代码stu2运行结果相同,into gs这个gs存放的就是和stu匹配的tea对象。
DefaultIfEmpty()
如果序列为空,则返回一个具有默认值的单例类集合。
详情可见msdn
在外联查询中的表现,我们去掉gs后的DefaultIfEmpty(),再次运行程序
var stuTeas = from stu in stus
join tea in teas on stu.TeacherID equals tea.Id into gs
from g in gs
select new StuTea
{
StuId = stu.Id,
StuName = stu.Name,
TeaId = g == null ? -1 : g.Id,
TeaName = g == null ? "" : g.Name
};
foreach (var stuTea in stuTeas)
{
Console.WriteLine(stuTea);
}
结果为
学生id:1,学生name:张三,老师id:1,老师name:刘老师
学生id:2,学生name:李四,老师id:1,老师name:刘老师
学生id:3,学生name:王五,老师id:2,老师name:徐老师
以老师查询
//外连接
var stuTeas = from tea in teas
join stu in stus on tea.Id equals stu.TeacherID into gs
from g in gs.DefaultIfEmpty()
select new StuTea
{
StuId = g == null ? -1 :g.Id,
StuName = g == null ? "" :g.Name,
TeaId = tea.Id,
TeaName = tea.Name
};
foreach (var stuTea in stuTeas)
{
Console.WriteLine(stuTea);
}
运行结果
学生id:1,学生name:张三,老师id:1,老师name:刘老师
学生id:2,学生name:李四,老师id:1,老师name:刘老师
学生id:3,学生name:王五,老师id:2,老师name:徐老师
学生id:-1,学生name:,老师id:4,老师name:杨老师
以老师为例查询代码解析
这个更容易看出,gs中存放的就是tea.Id ==stu.TeacherID的所有stu对象
var stuTeas = from tea in teas
join stu in stus on tea.Id equals stu.TeacherID into gs
select new
{
Stus = gs,
Tea = tea
};
foreach (var stuTea in stuTeas)
{
Console.WriteLine(stuTea.Tea.Id + stuTea.Tea.Name);
foreach (var stu in stuTea.Stus)
{
Console.WriteLine(" " + stu.Id + stu.Name);
}
}
运行结果
1刘老师
1张三
2李四
2徐老师
3王五
4杨老师