回顾上节:
1. from语句:查询用,在一个查询中如需要查询多个数据源,则使用多个from
注意这个多数据源的意思,除第一个数据源外,追加的数据源可以是上个数据源的变量,也可以是有别于上一级数据源的其它数据源。
2. 类型推断:LINQ查询最初的数据类型由数据源决定,但最后的的数据类型由select子句决定
3. select语句:定义最终要查询的数据以及将结果插入到结果序列中。
注意这里的,查询结果可能是一个变量也可以是一个集合,这个集合可以是变量也可以是一个匿名对象或者说是一个投影。
4. Group语句:分组,将查询结果按照指定分组条件放入不同的组中。
5. Into关键字:使用into上下文关键创建一个临时标识符,以便将group, join, 或 select子句的结果存储到新的标识符中。
6. Let子句:在查询增加一个范围变量。可以是直接运算来的, 也可以调用外部方法。
本节主要内容:
连接查询
1. join子句:将来自不同源序列并且在对象模型中没有直接关系的元素相关联
要求:每个源中的元素需要共享某个可以进行比较以判断是否相等的值
equals 关键字:比较指定的键是否相等
join子句的输出形式取决于所执行的连接的具体类型。以下是三种最常见的连接类型:
1. 内部连接
如:
var class_stu4=
from stu in students
join cls in class on stu.Cid equals cls.Id
select new{
Id=stu.Id,
Name=stu.Name,
Cname=cls.Name
};
其中:join cls in class on stu.Cid 语句中,join关键字连接,on关键字指示对比条件,equals关键字比较两个字段。
同时注意:两个数据源class和students,及equals两个边的字段的左右顺序,左边是第一个查询中的字段,右边为第二个查询中的字段(实践中可以试着调换后的结果是什么样的)。
2. 分组连接
含有into表达式的join子句称为分组连接
如:
var class_stu4=
from cls in class
join stu in students on cls.Id equals stu.Cid into groupstu
select new{
Name=cls.Name,
Stu=groupstu //这里是集合哦
};
注意:上例中第一个查询是class,第二个是students,相应equals关键字两边字段的顺序也要换
3. 左外连接
左外连接中,将返回左侧源序列中的所有元素。
若要在LINQ中执行左外连接,需要将DefaultIfEmpty方法与分组连接结合起来。
如:
var class_stu5=
from cls in class
join stu in students on cls.Id equals stu.Cid into groupstu
from item in groupstu.DefaultIfEmpty()
select new{
Cname=cls.Name,
stus=item //这是一个学生对象,当为空时报错
};
或
var class_stu5=
from cls in class
join stu in students on cls.Id equals stu.Cid into groupstu
from item in groupstu.DefaultIfEmpty(new Student {
Id=0,
Name="本班级下没有学生",
Cid=0
})
select new{
Cname=cls.Name,
stus=item.Name //这里是字段
};
更多的LINQ运算
1. 聚合运算
聚合运算即是从值集合计算出单个值
方法有:
1. Aggregate:对集合值执行自定义聚合运算
2. Average:计算集合平均值
3. Count:对集合中的元素进行计数,还可以仅对满足某一谓词函数的元素进行计数
4. LongCount:对大型集合中的元素进行计数,还可以仅对满足某一谓词函数的元素进行计数
5. Max:确定集合中的最大值
6. Min:确定集合中的最小值
7. Sum:计算集合中值的总和
如:
var students=new {
name="张三",
score=new List<int>{50,60,80,90}
};//使用匿名类作为数据源
double stu_avg=(
from sco in students.score
select sco
).Average();
2. 串联运算(两个物理文件内容串联)
注意新控件的使用:openFileDialog
注意texbox控件的MultiLine属性,即多行内容显示,或可输入多行内容。
如:合并文件
if(openFileDialog1.ShowDialog()==DialogResult.OK)
{
tb_a.Text=openFileDialog1.FileName;
}//如果打开的文件访问对话框返回的值是OK, 则将文件的名字赋值给文本控件的内容属性。
if(openFileDialog1.ShowDialog()==DialogResult.OK)
{
tb_b.Text=openFileDialog1.FileName;
}//解释同上
//接下来是合并两个文件的内容
string txtA=System.IO.File.ReadAllText(tb_a.Text);//这里是表示将文本中的内容全部读入到变量
string txtB=System.IO.File.ReadAllText(tb_b.Text);
//下面这两句是将文本内容按逗号进行分隔,然后装入数组变量中。
string[] strsA=txtA.Split(',');
string[] strsB=txtB.Split(',');
IEnumerable<string> concatQuery=
strsA.Concat(strsB).OrderBy(t => t).Distinct();//注意关键字Concat是用于连接字符串数组的,OrderBy是用于排序的,Distinct是去除重复内容的关键字
StringBuilder sb=new StringBuilder();
foreach(string s in concatQuery)
{
sb.AppendFormat("{0},",s)
}
textBox4.Text=sb.ToString();
3. 类型转换
如:
List<string> names=
(
from stu in students
select stu.Name
).Cast<string>().ToList<string>();
注意:关键字Cast类型转换功能的用法
4. lambda与LINQ
lambda表达式树允许开发人员像处理数据一样对lambda表达式进行修改。
参数 => 语句或语句块
<x,y> => x*y //多参数,隐式类型 => 表达式
x => x*5 //单参数,隐式类型转换 => 表达式
x => {return x*5} //单参数,隐式类型 => 语句块
(int x) => x*5 //单参数,显式类型 => 表达式
(int x) => {return x*5} //单参数,显式类型 => 表达式
() => Consol.WriteLine() //无参数
用法:
int[] arry={1,3,5,6,7,8,5};
//LINQ的写法
var res=
from i in arry
select i;
//Lambda写法
var res=arry.Select(i => i);
//LINQ的写法
var res=
from s in arry
where s%2==0
select s;
//Lambda写法
var res=arry.where(s=>s%2==0);
Lambda表达式其实就是一个匿名委托,可以使用Lambda表达式的地方必须是可以使用委托的地方。
5. 表达式树
编写Lambda表达式树
Lambda表达式树就是Lambda表达式转换成树状结构
Func<int,int> func=pra => pra * pra
//创建表达式树
Expression<Func<int,int>> expression = pra => pra*pra
//创建表达式树
当编译器编译Lambda表达式时,如果Lambda表达式使用的是Func方法,则编译器会将Lambda表达式直接编译成匿名方法,而如果Lambda表达式使用的是Expression方法,则编译器会将Lambda表达式进行分析,处理然后得到一种数据结构。
后的实例待补充,这是今天在工作业务练习的,后面的以窗口程序来做。
1. from语句:查询用,在一个查询中如需要查询多个数据源,则使用多个from
注意这个多数据源的意思,除第一个数据源外,追加的数据源可以是上个数据源的变量,也可以是有别于上一级数据源的其它数据源。
2. 类型推断:LINQ查询最初的数据类型由数据源决定,但最后的的数据类型由select子句决定
3. select语句:定义最终要查询的数据以及将结果插入到结果序列中。
注意这里的,查询结果可能是一个变量也可以是一个集合,这个集合可以是变量也可以是一个匿名对象或者说是一个投影。
4. Group语句:分组,将查询结果按照指定分组条件放入不同的组中。
5. Into关键字:使用into上下文关键创建一个临时标识符,以便将group, join, 或 select子句的结果存储到新的标识符中。
6. Let子句:在查询增加一个范围变量。可以是直接运算来的, 也可以调用外部方法。
本节主要内容:
连接查询
1. join子句:将来自不同源序列并且在对象模型中没有直接关系的元素相关联
要求:每个源中的元素需要共享某个可以进行比较以判断是否相等的值
equals 关键字:比较指定的键是否相等
join子句的输出形式取决于所执行的连接的具体类型。以下是三种最常见的连接类型:
1. 内部连接
如:
var class_stu4=
from stu in students
join cls in class on stu.Cid equals cls.Id
select new{
Id=stu.Id,
Name=stu.Name,
Cname=cls.Name
};
其中:join cls in class on stu.Cid 语句中,join关键字连接,on关键字指示对比条件,equals关键字比较两个字段。
同时注意:两个数据源class和students,及equals两个边的字段的左右顺序,左边是第一个查询中的字段,右边为第二个查询中的字段(实践中可以试着调换后的结果是什么样的)。
2. 分组连接
含有into表达式的join子句称为分组连接
如:
var class_stu4=
from cls in class
join stu in students on cls.Id equals stu.Cid into groupstu
select new{
Name=cls.Name,
Stu=groupstu //这里是集合哦
};
注意:上例中第一个查询是class,第二个是students,相应equals关键字两边字段的顺序也要换
3. 左外连接
左外连接中,将返回左侧源序列中的所有元素。
若要在LINQ中执行左外连接,需要将DefaultIfEmpty方法与分组连接结合起来。
如:
var class_stu5=
from cls in class
join stu in students on cls.Id equals stu.Cid into groupstu
from item in groupstu.DefaultIfEmpty()
select new{
Cname=cls.Name,
stus=item //这是一个学生对象,当为空时报错
};
或
var class_stu5=
from cls in class
join stu in students on cls.Id equals stu.Cid into groupstu
from item in groupstu.DefaultIfEmpty(new Student {
Id=0,
Name="本班级下没有学生",
Cid=0
})
select new{
Cname=cls.Name,
stus=item.Name //这里是字段
};
更多的LINQ运算
1. 聚合运算
聚合运算即是从值集合计算出单个值
方法有:
1. Aggregate:对集合值执行自定义聚合运算
2. Average:计算集合平均值
3. Count:对集合中的元素进行计数,还可以仅对满足某一谓词函数的元素进行计数
4. LongCount:对大型集合中的元素进行计数,还可以仅对满足某一谓词函数的元素进行计数
5. Max:确定集合中的最大值
6. Min:确定集合中的最小值
7. Sum:计算集合中值的总和
如:
var students=new {
name="张三",
score=new List<int>{50,60,80,90}
};//使用匿名类作为数据源
double stu_avg=(
from sco in students.score
select sco
).Average();
2. 串联运算(两个物理文件内容串联)
注意新控件的使用:openFileDialog
注意texbox控件的MultiLine属性,即多行内容显示,或可输入多行内容。
如:合并文件
if(openFileDialog1.ShowDialog()==DialogResult.OK)
{
tb_a.Text=openFileDialog1.FileName;
}//如果打开的文件访问对话框返回的值是OK, 则将文件的名字赋值给文本控件的内容属性。
if(openFileDialog1.ShowDialog()==DialogResult.OK)
{
tb_b.Text=openFileDialog1.FileName;
}//解释同上
//接下来是合并两个文件的内容
string txtA=System.IO.File.ReadAllText(tb_a.Text);//这里是表示将文本中的内容全部读入到变量
string txtB=System.IO.File.ReadAllText(tb_b.Text);
//下面这两句是将文本内容按逗号进行分隔,然后装入数组变量中。
string[] strsA=txtA.Split(',');
string[] strsB=txtB.Split(',');
IEnumerable<string> concatQuery=
strsA.Concat(strsB).OrderBy(t => t).Distinct();//注意关键字Concat是用于连接字符串数组的,OrderBy是用于排序的,Distinct是去除重复内容的关键字
StringBuilder sb=new StringBuilder();
foreach(string s in concatQuery)
{
sb.AppendFormat("{0},",s)
}
textBox4.Text=sb.ToString();
3. 类型转换
如:
List<string> names=
(
from stu in students
select stu.Name
).Cast<string>().ToList<string>();
注意:关键字Cast类型转换功能的用法
4. lambda与LINQ
lambda表达式树允许开发人员像处理数据一样对lambda表达式进行修改。
参数 => 语句或语句块
<x,y> => x*y //多参数,隐式类型 => 表达式
x => x*5 //单参数,隐式类型转换 => 表达式
x => {return x*5} //单参数,隐式类型 => 语句块
(int x) => x*5 //单参数,显式类型 => 表达式
(int x) => {return x*5} //单参数,显式类型 => 表达式
() => Consol.WriteLine() //无参数
用法:
int[] arry={1,3,5,6,7,8,5};
//LINQ的写法
var res=
from i in arry
select i;
//Lambda写法
var res=arry.Select(i => i);
//LINQ的写法
var res=
from s in arry
where s%2==0
select s;
//Lambda写法
var res=arry.where(s=>s%2==0);
Lambda表达式其实就是一个匿名委托,可以使用Lambda表达式的地方必须是可以使用委托的地方。
5. 表达式树
编写Lambda表达式树
Lambda表达式树就是Lambda表达式转换成树状结构
Func<int,int> func=pra => pra * pra
//创建表达式树
Expression<Func<int,int>> expression = pra => pra*pra
//创建表达式树
当编译器编译Lambda表达式时,如果Lambda表达式使用的是Func方法,则编译器会将Lambda表达式直接编译成匿名方法,而如果Lambda表达式使用的是Expression方法,则编译器会将Lambda表达式进行分析,处理然后得到一种数据结构。
不废话看实例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Con35
{
class Program
{
static void Main(string[] args)
{
//跟前面几个例子似的,先创建几个对象集合出来
//说实话,老师讲的我都看的懂,但是。。。。看的懂真的不一定会写
//一碰键盘脑袋空空啊,还是认认真真的自己敲出来实惠些
List<Banji> bjs = new List<Banji>() {
new Banji("1001","小学一年级"),
new Banji("1002","小学二年级"),
new Banji("1003","小学三年级"),
new Banji("1004","小学四年级"),
new Banji("1005","小学五年级"),
new Banji("1006","初中一年级"),
new Banji("1007","初中二年级"),
new Banji("1008","初中三年级"),
new Banji("1009","初中四年级")
};
List<student> stus = new List<student>() {
new student(1,"张小三",new List<int>(){33,78,90},"1001"),
new student(2,"王小三",new List<int>(){65,66,67},"1002"),
new student(3,"李小三",new List<int>(){54,78,67},"1003"),
new student(4,"赵小三",new List<int>(){65,32,21},"1004"),
new student(5,"刘小三",new List<int>(){90,78,90},"1005"),
new student(6,"田小三",new List<int>(){65,78,88},"1006"),
new student(7,"张中三",new List<int>(){65,44,90},"1007"),
new student(8,"王中三",new List<int>(){55,78,90},"1008"),
new student(9,"张小四",new List<int>(){65,78,89},"1008"),
new student(20,"张小五",new List<int>(){65,44,77},"1005"),
new student(10,"张小六",new List<int>(){32,78,90},"1004"),
new student(11,"张小七",new List<int>(){65,55,66},"1007"),
new student(12,"张小八",new List<int>(){70,78,77},"1002")
};
Console.WriteLine("\n========join...on...equals.===============");
var stuCollections = from bj in bjs
where bj.bjNum != "1005"//这里加个排除班级编号为1005的班级
join st in stus on bj.bjNum equals st.bjNum
select new { na = st.stuName, bn = bj.bjName, bu = bj.bjNum };
foreach (var v in stuCollections)
{
Console.WriteLine("学生名({0}, 班级名({1})),班级编号是({2})", v.na, v.bn, v.bu);
}
Console.WriteLine("\n========join...on...equals...into===============");
var stuCol = from bj in bjs
where bj.bjName != "初中三年级"
join st in stus on bj.bjNum equals st.bjNum into col
select new { id = bj.bjNum, bn = bj.bjName, col };
foreach (var v in stuCol)
{
//需要进行两次迭代,啥是迭代?啥是循环?一个接一个不用报告情况直到解决问题,而循环是一个又一个,即
//时时回报情况,这是我对迭代和循环的理解
Console.WriteLine("\n=====班级编号({0}),班级名字({1})======\n", v.id, v.bn);
//迭代学生集合
foreach (student s in v.col)
{
Console.WriteLine("学生编号({0}),学生姓名({1}),学生所在班级({2})", s.stuId, s.stuName, s.bjNum);
}
}
Console.WriteLine("\n====外连接相关,必须要考虑到空值,未加默认值=======");
//这里我复制了join...on... into中的代码进来,稍加改动
var stuCols = from bj in bjs
where bj.bjName != "初中三年级"
join st in stus on bj.bjNum equals st.bjNum into col//下面加了段空值判断语句
from item in col.DefaultIfEmpty()//这里没有对其指定默认值
select new { id = bj.bjNum, bn = bj.bjName, test = item };
foreach (var v in stuCols)
{
Console.WriteLine("\n=====班级编号({0}),班级名字({1})======\n", v.id, v.bn);
//Console.WriteLine("学生编号({0}),学生姓名({1}),学生所在班级({2})", v.test.stuId, v.test.stuName, v.test.bjNum);//如果item为Null那运行时就会报错,我还自以为不会,实践出真知,真理也!
Console.WriteLine("学生编号({0}),学生姓名({1}),学生所在班级({2})", v.test != null ? v.test.stuId : 0, v.test != null ? v.test.stuName : "无", v.test != null ? v.test.bjNum : "空空空");//这里为每个对象属性加个条件判断
}
Console.WriteLine("\n====外连接相关,必须要考虑到空值,追加默认值=======");
//这里我复制了join...on... into中的代码进来,稍加改动
var stuCol2 = from bj in bjs
where bj.bjName != "初中三年级"
join st in stus on bj.bjNum equals st.bjNum into col//下面加了段空值判断语句
from item in col.DefaultIfEmpty(
new student(0,"万能的神",new List<int>(){0,0,0},"无")
)//指定默认值
select new { id = bj.bjNum, bn = bj.bjName, test = item };
foreach (var v in stuCol2)
{
Console.WriteLine("班级信息({0}号,{1}),学生信息({2}号学生 {3},所属班级编号是({4}))"
,v.id,v.bn,v.test.stuId,v.test.stuName,v.test.bjNum
);
}
Console.WriteLine("\n======聚合函数=======");
//老师很细心的又让我复习了一下匿名类
var stu = new
{
name = "朱八戒",
score = new List<int>() { 90, 45, 98, 34, 78, 99 }
};
//不知道有没有人不明白,下面为什么我这么肯定要用double数据类型?
//这里面有个关系,上面stu这个对象类型决定了val的类型
//接下来,slect的类型决定了maxValue的数据类型
//而maxValue的类型就决定了foreach或着输出的数据类型
double maxValue = (from val in stu.score
select val).Max();
Console.WriteLine("最大值是{0}", maxValue.ToString());
Console.ReadKey();
}
}
public class Banji
{
public string bjNum;//班级编号
public string bjName;//班级名字
public Banji(string bnu, string bna)
{
this.bjName = bna;
this.bjNum = bnu;
}
}
public class student
{
public int stuId;//学生编号
public string stuName;//学生姓名
public List<int> score;//学生成绩单
public string bjNum;//班级编号
public student(int sid, string sna, List<int> sco, string bjn)
{
this.stuId = sid;
this.stuName = sna;
this.score = sco;
this.bjNum = bjn;
}
}
}
后的实例待补充,这是今天在工作业务练习的,后面的以窗口程序来做。