C#之Linq学习笔记
①什么是LINQ
②LINQ的组成
①自动属性
②对象初始化器
③隐式类型变量
12. 使用linq to sql实现对远程数据库的查询(4.14
13.使用linq to sql实现数据的新增,删除(4.14
①常用控件
②datetimepicker:日期时间控件
④timer:定时器控件
⑤HScrollBar:水平滚动条
⑥ListView(4.15,4.16)
⑦树视图控件:TreeView(4.16)
LINQ(Language Integrated Query):语言集成化查询
LINQ中提供了一组通用的API(Application Programming Interface:应用程序编程接口)
使用LINQ可以对于各种不同的数据源,实现统一的查询。数据源可以是对象集合(内存),SQL Server数据库,XML文件。
根据数据源的不同,LINQ主要包含三个部分
1.LINQ to Objects:主要负责对于集合对象的查询
2.LINQ to XML:主要负责对于XML文件的查询
3.LINQ to ADO.NET:主要负责数据库的查询
(1)LINQ to SQL(直接对数据库查询)
(2)LINQ to DataSet(对本地数据集查询)
自动属性:采用自动属性不需要定义内部私有字段,编译器会自动生成私有字段
示例:(Demo2)
class Person
{
public string Name
{
get;set;
}
public int Age
{
get;set;
}
}
Person p1 = new Person();
通过自动属性,可以自动生成的私有字段赋值
p1.Name = "jack";
p1.Age = 20;
Console.WriteLine($"姓名:{p1.Name}\t年龄:{p1.Age}");
对象初始化器:给对象的属性赋值
示例:(demo3)
使用对象初始化器给对象的属性初始化
Person p1 = new Person { Name = "jack", Age = 20,Sex="男" };
Person p2 = new Person { Name = "tom", Age = 21 };
集合初始化器
List<Person> lstPersons = new List<Person>
{
new Person { Name = "jack", Age = 20, Sex = "男",Address=new Address {Province="江苏省",City="苏州市"} },//里面的每个对象使用对象初始化器初始化
new Person { Name = "tom", Age = 21, Sex = "男" },
new Person { Name = "lucy", Age = 20, Sex = "女" }
};
隐式类型变量:使用var关键,通过给变量赋的值,编译器会根据初始化的值推断变量的类型
注意:
(1)必须对变量初始化,初始化的值不能是null
(2)隐式类型变量只能是局部变量,不能是类中的成员变量
(3)隐式类型变量确定数据类型之后,只能赋值该类型的数据,不能随意赋值
匿名类型:在实例化对象时,无需指定一个具体的类型,编译器会根据创建对象时的属性个数和赋的值自动生成一个匿名类型
示例:(Demo5)
var p = new { Name = "jack", Age = 20, Sex = "男" };
如果一个类已经定义好,并且在其中定义了一些方法,如果需要为这个类增加新的方法,根据“开--闭原则”不建议修改原来类中的代码
第一种办法:定义一个新的类,让它继承自原来的类,在新类中添加新的方法
第二种方法:使用扩展方法,定义一个新的类,但不需要继承原来的类,在该类中定义新的方法,这个方法就叫做扩展方法
扩展方法的定义:
(1)扩展方法定义在静态类中
(2)扩展方法定义是以静态方法的形式定义
(3)扩展方法的第一个参数:this 对哪种类型扩展,其余的参数是实际的参数,在调用时需要提供匹配的实参
示例:(Demo6)
//扩展方法定义是以静态方法的形式定义
//第一个参数:this 对哪种类型扩展
public static string Upper(this string s)
{
string first = s.Substring(0, 1).ToUpper();
string left = s.Substring(1).ToLower();
return first + left;
}
//通过参数个数的不同,扩展方法形成方法重载
//第一个参数:this 对哪种类型扩展
//第二个参数:i是真正的参数
public static string Upper(this string s,int i)
{
string first = s.Substring(0, i).ToUpper();
string left = s.Substring(i).ToLower();
return first + left;
}
//定义string类型的实例
//string str = "hello";
//通过实例名.扩展方法调用,此时该实例str,就传递给了扩展方法的第一个参数s
//Console.WriteLine(str.Upper());
//string name = "jack";
//name = name.Upper();
//Console.WriteLine(name);
string str = "tonny";
str = str.Upper(1);
Console.WriteLine(str);
Console.ReadKey();
定义一个没有名字的方法
语法:
delegate(参数列表)
{
//方法体
}
示例:(Demo8)
1)定义一个委托,该委托可以代理具有一个string类型参数,并且返回值是string的方法
public delegate string StringDelegate(string str);
2)创建委托实例,指向一个匿名方法
StringDelegate sd = delegate (string s)
{
return s.ToLower();
};
语法:
(参数列表) => {方法体}
注意:
(1)参数列表中的参数类型可以是明确类型或者是推断类型,如果是推断类型,则参数的数据类型将由编译器根据上下文自动推断出来
(2)只能一个参数时,可以省略( )
(3)方法体只包含一条语句时,可以省略{ }:
(参数列表) => {方法体}
变成:(参数列表) =>表达式
示例:(Demo9)
//创建委托实例,指向代理的方法(lambda表达式)
//lambda:=>(goes to)
//StringDelegate sd =
(string s) =>
{ Console.WriteLine("正在转换小写字符串..."); return s.ToLower(); };
//可以省略参数的类型声明,因为能够根据上下文推断出参数的类型
StringDelegate sd = (s) => { Console.WriteLine("正在转换小写字符串..."); return s.ToLower(); };
//如果只有一个参数,可以省略( )
StringDelegate sd = s => { Console.WriteLine("正在转换小写字符串..."); return s.ToLower(); };
如果方法体中只有一条语句,可以省略{}
StringDelegate sd = s => s.ToUpper();
示例:(Demo11)
//1.获取数据源
List<string> lst = new List<string> {"jack","tom","lucy","lili" };
//2.定义查询
var query = lst.Select(s => s.ToUpper());
//3.执行查询
foreach (var item in query)
{
Console.WriteLine(item);
}
示例:(Demo12)
构造数据源
List<Student> lstStudent = new List<Student>
{
new Student {No="001",Name="jack",Sex="男",Age=20,ClassNo=1 },
new Student {No="002",Name="tom",Sex="男",Age=21,ClassNo=1 },
new Student {No="003",Name="lucy",Sex="女",Age=21,ClassNo=2 },
new Student {No="004",Name="lili",Sex="女",Age=22,ClassNo=3 },
new Student {No="005",Name="tonny",Sex="男",Age=24,ClassNo=3 }
};
1.Select():获取目标数据
//1查询出所有学员的姓名
//var query = lstStudent.Select(s => s.Name);
//2查询出所有学员的基本信息(包括各个属性值)
//var query = lstStudent.Select(s => s);
//3查询出所有学员的学号和姓名
//var query = lstStudent.Select(s => s.No + ":" + s.Name);
//4查询出所有学员的基本信息,并且返回匿名类型
//var query = lstStudent.Select(s => new { no= s.No, name = s.Name, sex = s.Sex });
2.Where():筛选数据
查询所有男生的基本信息
//var query = lstStudent.Where(s => s.Sex == "男")
// .Select(s => s);
查询出所有年龄大于20岁的男生基本信息
//var query = lstStudent.Where(s => s.Age > 20)
// .Where(s => s.Sex == "男")
// .Select(s => s);
上面的这个查询语句可以写成:
//var query = lstStudent.Where(s => s.Age > 20 && s.Sex=="男")
// .Select(s => s);
3.FirstOrDefault():获得满足筛选条件的第1个元素,如果没有满足条件的元素,返回默认值
查询学号是001的学员
//var student = lstStudent.FirstOrDefault(s => s.No == "001");
4.OrderBy():按指定字段升序排序
查询所有名字是4个字符的学员基本信息,并且按照年龄升序排序
//var query = lstStudent.Where(s => s.Name.Length == 4)
// .OrderBy(s => s.Age)
// .Select(s => s);
5.OrderByDescending():按指定字段降序排序
//var query = lstStudent.Where(s => s.Name.Length == 4)
// .OrderByDescending(s=>s.Age)
// .Select(s => s);
6.ThenBy():对元素进行后续升序排序
var query = lstStudent.OrderByDescending(s => s.Age)
// .ThenBy(s => s.Name)
// .Select(s => s);
7.ThenByDescending():对元素进行后续升序排序
var query = lstStudent.OrderByDescending(s => s.Age)
.ThenByDescending(s => s.Name)
.Select(s => s);
8. GroupBy():分组查询(4.14)
//IEnumerable<IGrouping<string,Student>> query = lstStudent.GroupBy(s => s.Sex);
foreach (var item in query1)
{
//item:按照指定的分组字段分组之后的集合
Console.WriteLine(item.Key); //Key:分组的关键字
//item变量由于它的类型是IGrouping<string,Student>,这种类型也实现了IEnumerable<TElement>类型接口
//所以对于item集合也可以继续使用linq中的方法去查询
var subQuery = item.Select(s => s).OrderBy(s => s.Age);
foreach (var s in subQuery)
{
Console.WriteLine(s.ToString());
}
Console.WriteLine("------------------------------------------");
}
9. 立即执行的方法
//var query = lstStudent.Where(s=>s.Sex=="男")
// .Count();
//query = lstStudent.Count(s => s.Sex == "男");
//var query = lstStudent.Average(s => s.Age);
//query = lstStudent.Max(s => s.Age);
//query = lstStudent.Min(s => s.Age);
//query = lstStudent.Sum(s => s.Age);
10. Join()实现连接查询
lstClass集合是第一个数据源对象
var query = lstClass.Join(
lstStudent, //第一个参数:连接的第二个数据源对象,必须实现
c =>c.ClassNo, //第二个参数:从第一个数据源的每个元素中提取连接条件的函数
s=>s.ClassNo, //第三个参数:从第二个数据源的每个元素中提取连接条件的函数
(c, s) =>new {c.ClassName,s.No,s.Name,s.Sex}//第四个参数:连接之后的输出结果,在这里以匿名类型返回
);
//也可以使用下面的方式连接
var query = lstStudent.Join(
lstClass,
s => s.ClassNo,
c => c.ClassNo,
(s, c) => new { c.ClassName, s.No, s.Name, s.Sex }
);
foreach (var item in query) {
Console.WriteLine(item.ClassName+"\t"+item.No+"\t"+item.Name+"\t"+item.Sex);
}
语法更接近于sql查询的语法,可读性更强
语法糖,底层是需要将查询语句转换成查询方法
常用的查询语句:
from 指定要查找的数据源以及范围变量
select 指定要返回的目标数据 ------------>Select()方法
where 指出筛选条件,多个where是且的关系 ------------->Where()方法
orderby 设置排序字段或表达式,以及排序的方式
ascending ------------->OrderBy()方法
descending ------------->OrderByDescending()方法
次级排序ascending ------------->ThenBy()方法
次级排序descending ------------->ThenByDescending()方法
group element by key:按照指定的key分组,并返回element类型
-------------->GroupBy()方法
join: 数据源连接 --------------->Join()方法
没有对应的查询语句 ---------------->Count(),Max(),Min(),Average(),Sum()
示例:(Demo2)
1查询出所有学员的基本信息
//var query = from s in lstStudent
// select s;
//foreach (var item in query)
//{
// Console.WriteLine(item.ToString());
//}
2查询出所有学员的姓名
//var query = from s in lstStudent
// select s.Name;
//foreach (var item in query)
//{
// Console.WriteLine(item);
//}
3查询出所有学员的学号、姓名、性别
//var query = from s in lstStudent
// select new { s.No, s.Name, s.Sex };
//foreach (var item in query)
//{
// Console.WriteLine(item.No+"\t"+item.Name+"\t"+item.Sex);
//}
4查询所有年龄大于20岁的男生的基本信息
//var query = from s in lstStudent
// where s.Age > 20
// where s.Sex == "男"
// select s;
//var query = from s in lstStudent
// where s.Age > 20 && s.Sex=="男"
// select s;
5查询出年龄大于20岁的学员,并按年龄升序排序
//var query = from s in lstStudent
// where s.Age > 20
// orderby s.Age ascending
// select s;
//var query = from s in lstStudent
// where s.Age > 20
// orderby s.Age descending //降序
// select s;
//var query = from s in lstStudent
// where s.Age > 20
// orderby s.Age descending,s.Name ascending //先按年龄降序,年龄相同,再按姓名降序
// select s;
//foreach (var item in query)
//{
// Console.WriteLine(item.ToString());
//}
6按照班级编号分组
//var query = from s in lstStudent
// where s.Age>20
// group s by s.ClassNo;
//foreach (var item in query)
//{
// Console.WriteLine(item.Key);
// foreach (var s in item)
// {
// Console.WriteLine(s.No+"\t"+s.Name+"\t"+s.Sex+"\t"+s.Age);
// }
// Console.WriteLine("-------------------------------------------------");
//}
7内连接
//var query = from s in lstStudent
// join c in lstClass on s.ClassNo equals c.ClassNo
// select new
// {
// c.ClassName,
// s.No,
// s.Name,
// s.Sex,
// s.Age
// };
8统计所有男生的人数
//1)使用查询方法实现
var count = lstStudent.Where(s => s.Sex == "男")
.Count();
//2)使用查询方法+查询语句的混合方式实现
count = (from s in lstStudent
where s.Sex == "男"
select s).Count();
连接步骤:(Demo3)
1.linq to sql:直接对于sql server数据库查询
2.对于关系型的数据库----->以OOP思想实现
将数据库中的表映射成实体类
将表中的字段映射成实体类的属性
将数据库中表与表之间的外键关系也做了映射成导航属性
3.linq to sql使用步骤:
1)添加linq to sql类,扩展名是dbml
2)在服务器资源管理器中数据连接--->右键--->添加连接--->选择Microsoft SQL Server数据源-->输入服务器名称--->选择身份验证--->选择数据库--->测试连接
3)选中数据表按住鼠标左键,拖放至dbml设计界面,例如拖动了student表和class表
4)将数据库中的表映射成实体类:
此时会生成两个实体类,分别是student表映射的student类,以及class表映射的@class类
5)将表中的字段映射成实体类的属性:
此时在student类中会自动生成属性,这些属性就是student表中的字段映射的属性
在@class类中会自动生成属性,这些属性就是class表中的字段映射的属性
6)将数据库中表与表之间的外键关系也做了映射成导航属性:
在student实体中会生成一个导航属性:
public @class @class //这个学生对象所在的班级对象
{
get
{
return this._class.Entity;
}
set
{
@class previousValue = this._class.Entity;
if (((previousValue != value)
|| (this._class.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this._class.Entity = null;
previousValue.student.Remove(this);
}
this._class.Entity = value;
if ((value != null))
{
value.student.Add(this);
this._classNo = value.classNo;
}
else
{
this._classNo = default(string);
}
this.SendPropertyChanged("@class");
}
}
}
在@class表中生成一个导航属性:
public EntitySet<student> student //一个班级对象下面的学生集合
{
get
{
return this._student;
}
set
{
this._student.Assign(value);
}
}
7)将数据库映射成一个数据上下文类,继承自System.Data.Linq.DataContext类
自动生成构造函数
将数据库表中的数据映射成Table<TEntity>类型集合
比如在这里生成了两个集合,分别是:
//数据库中student表的数据集合
public System.Data.Linq.Table<student> student
{
get
{
return this.GetTable<student>();
}
}
//数据库中class表的数据集合
public System.Data.Linq.Table<@class> @class
{
get
{
return this.GetTable<@class>();
}
}
12. 使用linq to sql实现对远程数据库的查询(4.14)
步骤:(Demo3)
1) 实例化数据上下文对象
2)获取数据源:上下文对象.属性--->Table<TEntity> ,由于Table<TEntity>实现了IEnumerable<TEntity>接口,所以这个数据源可以使用linq的查询方式
方法一:var students = db.student;
方法二:List<student> lstStudent = db.student.ToList();
3)把对于数据库的查询转换变为使用linq方式实现,根据需要定义查询语句
4)执行查询(延迟执行)
13.使用linq to sql实现数据的新增,删除(4.14)
1)新增数据
实例化上下文对象db
StuDBDataContext db = new StuDBDataContext();
构造实体对象
db.student.InsertOnSubmit(实体对象)
db.SubmitChanges()实现真正的插入操作,此时会生成插入语句发送到数据库执行
示例:
//将窗体中输入的学员信息,封装成一个学员对象
student stu = new student
{
sno = txtsno.Text,
sname = txtName.Text,
sex = cmoSex.SelectedItem.ToString(),
sId = txtId.Text,
semail = txtEmail.Text,
saddress = txtAddress.Text,
sbirthday = dtpBirthday.Value,
classNo = comClass.SelectedValue.ToString()
};
// 将这个对象插入至Table中,注意这时并没有向数据库发送插入语句,只是将这个实体标记为待插入的状态
db.student.InsertOnSubmit(stu);
db.SubmitChanges();//一定要调用这个方法
MessageBox.Show("新增成功");
2)删除数据
实例化上下文对象db
StuDBDataContext db = new StuDBDataContext();
从指定的表中找到待删除的对象db.student.FirstOrDefault()方法
db.student.DeleteOnSubmit(待删除的对象)
db.SubmitChanges()实现真正的删除操作,此时会生成删除语句发送到数据库执行
示例:
students.DeleteOnSubmit(stu);//此时并没有真正删除,只是将Table中的指定的实体标记为待删除的状态
db.SubmitChanges();//此时会根据Table中实体的状态,自动生成相应的语句发送到数据库执行
label:标签
textbox:文本框
radiobutton:单选按钮
checkbox:复选框
combobox:下拉列表
listbox:列表框
button:按钮
datetimepicker:日期时间控件
picturebox:图片框
listview:列表视图
datagridview:网格形式显示数据
contextmenustrip:快捷菜单
menustrip:主菜单
容器控件
panel
groupbox
tabcontrol
属性:
ShowUpDown:是否显示上下的微调按钮
Format:显示日期时间的格式
Value:获取或设置日期时间值
事件:
ValueChanged:改变日期时间时触发
③ProcessBar:进度条控件
属性:
Value:获取或设置进度条目前的值
Step:进度条的步数
Minimum:最小值
Maximnu:最大值
方法:
Increment(int value):按照指定的value值设置进度条的位置
PerforeStep():按照Step属性的值前进
属性:
Enable:是否启用定时器
Interval:时间间隔值,单位是毫秒
方法:
Start():启动定时器
Stop():停止定时器
事件:
Tick事件:当指定的时间间隔到达时触发的事件
属性:
Minimum:最小值
Maximnu:最大值
Value:滚动条的当前值
事件:
Scroll:滚动条滚动时触发
5.ImageList:图片列表
属性:
ImageSize:图片的大小
Images:图片集合
1)显示图片列表中的图片
2)分组操作
3)排序操作
调用Sort()方法,设置ListViewItemSorter属性是实现IComparer接口的比较器类
使用该控件显示具有层次结构的结点数据
属性:
CheckBoxes:给节点前添加复选框
Nodes:节点集合
SelectedNode:选中的节点
Parent:获得指定节点的父节点
事件:
AfterSelect:选中节点之后触发
1.在父窗体中可以打开子窗体
2.在父窗体中添加菜单栏:MenuStrip
工具栏:ToolStrip
状态栏:StatusStrip
1.ColorDialog:颜色对话框
2.FolderBrowserDialog:浏览文件夹对话框
3.FontDialog:字体对话框
4.OpenFileDialog:打开文件对话框
5.SaveFileDialog:另存为对话框