目录
一、Linq基础
1.LINQ能解决什么问题?
a.面向对象编程语言与数据访问方法长期分离,以嵌入式的方式开发。(嵌入的SQL语句没有任何提示,易出错)如
string sql = “select * from Student where StudentId = ”+studentId;
b.编程语言中的数据类型和数据库中的数据类型形成两套体系,如C#中的string在SQL中有NVarchar/Varchar/Char表示
c.SQL和XML都有各自的查询语言,而对象没有自己的查询语言,比如要从List<T>集合/数组中找到符合要求的元素,非常困难
2.什么是LINQ
- LINQ(Language Integrated Query,语言集成查询)
- 是微软公司提供的一项新技术,能欧冠将查询功能直接引入到C#、VB.NET等编程语言中
- 查询操作可以通过编程语言自身来表示,而不是嵌入字符串SQL语句
- LINQ主要包含以下三部分
- LINQ to Object是主要负责对象的查询
- LINQ to XML主要负责XML的查询
- LINQ to ADO.NET主要负责数据库的查询(重点)
- LINQ to SQL(目前已不在使用)
- LINQ to DataSet
- LINQ to Entities(重点学习)
- LINQ所在命名空间
- System.Linq;该命名空间已经由系统自动引入
- 因此微软默认建议多使用Linq技术查询
LINQ在数组中的操作
//不使用LINQ查询数组
static void Main(string[] args)//查询数组中的奇数
{
int[] nums = {1,3,5,6,2,4,15,21};
List<int> list = new Lint<int>();
foreach(int item in nums)
{
if(item%2 != 0)
list.Add(item);
}
list.Sort();
list.Reverse();
foreach(int item in list)
{
//输出list集合
}
}
//使用LINQ查询数组
static void Main(string[] args)//查询数组中的奇数
{
int[] nums = {1,3,5,6,2,4,15,21};
//List<int> list = new Lint<int>();
//foreach(int item in nums)
//{
// if(item%2 != 0)
// list.Add(item);
//}
//list.Sort();
//list.Reverse();
//LINQ查询
var list = from num in nums
where num%2!=0
orderby num descending
select num;
foreach(int item in list)
{
//输出list集合
}
}
二、LINQ查询方法
- 获取数据:扩展方法Select()
- Select()是一个泛型扩展方法
- Select()方法使用时要求传递一个委托实例(委托实例就是一个方法)
- Select()方法的应用
static void Main(string[] args) { int[] nums = {1,4,2,7,5,10,23}; var list = nums.Select(item => item*item); foreach(int i in list) { //输出list集合,结果为对应元素的平方 } } 1.Select()方法中是Lambda表达式 2.返回结果是一个迭代器(Iterator)
提示:数组、泛型集合都可以使用扩展方法Select()
- 筛选数据:Where()方法
- Where()方法是一个扩展类型方法
- Where()方法使用时需要传递一个委托实例,但该实例是一个判断条件,因此返回的类型必须是bool类型
//找寻数组中偶数,并输出其平方 static void Main(string[] args) { int[] nums = {1,4,2,7,9,12,13,20}; var list = nums .Where(item => item%2 == 0) .Select(i => i*i); //var list = nums.Where(item => item*item).Select(i => i*i);//链式编程的两种表达方式 foreach(int i in list) { //输出list集合 } }
- 排序数据:OrderBy()
- OrderBy()是一个扩展方法
- OrderBy()里面的参数要求传递一个排序的字段,默认采用升序排列
- 如果使用降序,使用OrderByDescending方法
//升序输出数组中偶数的平方 static void Main(string[] args) { int nums = {1,4,2,6,9,14,19}; var list = nums .Where(item => item%2 == 0) .Select(item => item*item) .OrderBy(item => item); foreach(int i in list) { //输出list集合 } }
//按照姓氏降序输出名字为两个字的姓名 static void Main(string[] args) { int[] nums = {"张三","王明林","李世桥","那英","成龙","张丽"}; var list = nums .Where(item => item.Length == 2) .Select(item => item) .OrderByDescending(item => item.Substring(0,1)); foreach(string i in list) { //输出list集合 } }
-
分组数据:GroupBy()方法
-
GroupBy()是扩展方法
-
GroupBy()里面的参数要求传递一个分组字段
//分组显示姓名 static void Main(string[] args) { string[] nums = {"张勇","王琦","成龙","巩俐","马冬梅","张弛","杜宇"}; var list = nums .Where(item => item.Length == 2) .Select(item => item) .GroupBy(item => item.Substring(0,1)); foreach(var groupItem in list) { Console.WriteLine("分组字段{0}",groupItem.Key); foreach(var item in groupItem ) { //分组输出集合 } } }
-
三、LINQ查询时机与查询形式
- LINQ查询时机
- 查询步骤:获取数据源、定义查询、执行查询
- 结论:定义查询后,查询并未立即执行,而是在遍历时真正执行查询,称为延迟执行(deferred execution)
- 使用"聚合扩展方法"返回单一结果,强制查询立即执行:如Count()
static void Main(string[] args) { int[] nums = {1,5,2,7,13,2,9,21}; var list = nums .Where(item => item%2 == 0) .Select(item => item*item) .OrderBy(item => item) .Count();//语句将立即执行查询 }
-
LINQ查询两种形式
-
Method Syntax,查询方法方式
-
主要利用System.Linq.Enumerable类中定义的扩展方法和Lambda表达式方式进行查询
-
-
Query Syntax,查询语句方式
-
一种更接近SQL语法的查询方式,可读性更好
-
查询语句最后还是要被翻译成查询方法
//查询语句 var list = from num in nums where num%2 != 0 orderby num descending select num; //查询方法 var list = nums .Where(item => item%2 != 0) .Select(item => item) .OrderByDescending(item => item);
两种查询的执行效果完全一样
-
-
两种形式的比较
-
CLR本身并不理解查询语句,只理解查询方法
-
编译器负责在编译时将查询语句翻译为查询方法
-
大部分查询方法都有对应的查询语句形式,如Select()suiying select
-
部分查询方法目前在C#中还没有对应的查询语句,如Count()和Max(),这时可以采用替代方案:
-
查询方法
-
查询语句+查询方法
-
-
-
一般情况下,建议使用可读性更好的查询语句
四、 LINQ查询子句
- LINQ查询字句的概述
- 查询表达式:是一种用查询语法表示的表达式,由一组用类似SQL的语法编写的句子组成,每个字句可以包含一个或多个C#表达式
//LINQ查询表达式必须以from子句开头 //并且必须以select或group子句结束,中间可以添加多个子句 var lsit = from num in nums where num%2 != 0 orderby num descending select num;
- LINQ查询表达式包含的子句
from子句:指定查询操作的数据源和范围变量
where子句:筛选元素的逻辑条件,返回值是一个bool类型
select子句:指定查询结果的类型和表现形式
orderby子句:对查询结果进行排序
group子句:对查询结果进行分组
into子句:提供一个临时标识符,该标识可以充当对join/group/select子句结果的引用
join子句:连接多个查询操作的数据源
let子句:引用用于存储查询表达式中的子表达式结果的范围变量
-
from子句
1.LINQ查询表达式必须包含from子句,并且必须以from子句开头,from子句指定的数据源类型必须为IEnumerable、Ienumerable<T>或者两者的派生类型(如:数组、List<T>、ArrayList等)
2.关于数据源,如果数据源是泛型类型,则编译器可以自动推算出范围变量的类型,如果数据源类型是非泛型类型,如ArrayList,则必须显示指定范围变量的数据类型
static void Main(string[] args) { ArrayList values = new ArrayList(); for(int i = 0;i < 10;i++){values.Add(i);} var list = from int item in values where item%2 != 0 select item; }
3.复合from子句查询:如果数据源(本身是一个序列)的元素还包含数据源(序列、列表等),如果要查询子数据源中的元素需要使用复合from子句
static void Main(string[] args) { Student obj1 = new Student(){StuId = 1001,StiName = "学员1",ScoreList = new List<int>(){90,75,60}}; Student obj2 = new Student(){StuId = 1002,StiName = "学员2",ScoreList = new List<int>(){97,83,70}}; Student obj3 = new Student(){StuId = 1003,StiName = "学员3",ScoreList = new List<int>(){80,89,96}}; List<Student> stuList = new List<Student>(){obj1,obj2,obj3}; var result = from stu in stuList from score in stu.ScoreList where score >= 95 select stu; foreach(var item in result) { //输出分数包含95分以上的学生 } } class Student { public int StuId{get;set;} public int Age{get;set;} public string StuName{get;set;} public List<int> ScoreList{get;set;} }
4.多个from子句查询:若LINQ查询表达式包含两个或两个以上的独立数据源时,可以使用多个from子句查询所有数据源中的数据
static void Main(string[] args) { Student obj1 = new Student(){StuId = 1001,StuName = "学员1"}; Student obj2 = new Student(){StuId = 1009,StuName = "学员9"}; Student obj3 = new Student(){StuId = 1012,StuName = "学员12"}; Student obj4 = new Student(){StuId = 1003,StuName = "学员3"}; Student obj5 = new Student(){StuId = 1019,StuName = "学员19"}; Student obj6 = new Student(){StuId = 1006,StuName = "学员6"}; List<Student> stuList1 = new List<Student>(){obj1,obj2,obj3}; List<Student> stuList2 = new List<Student>(){obj4,obj5,obj6}; //查询学号大于1010的学员 var result = from stuL1 in stuList1 where stu1.StuId > 1010 from stu2 in stuList2 where stu2.StuId> 1010 select new {stu1,stu2}; //显示结果 foreach(var item in result) { Console.WriteLine(item.stu1.StuName + " " +item.stu1.StuId); Console.WriteLine(item.stu2.StuName + " " +item.stu2.StuId); } }
- 查询表达式:是一种用查询语法表示的表达式,由一组用类似SQL的语法编写的句子组成,每个字句可以包含一个或多个C#表达式
五、LINQ高级查询
- 聚合类:Count,Max/Min,Average
//Count返回集合项的数目 static void Main(string[] args) { Student obj1 = new Student(){StuId = 1001,StuName = "学员1"}; Student obj2 = new Student(){StuId = 1009,StuName = "学员9"}; Student obj3 = new Student(){StuId = 1012,StuName = "学员12"}; Student obj4 = new Student(){StuId = 1013,StuName = "学员13"}; Student obj5 = new Student(){StuId = 1019,StuName = "学员19"}; Student obj6 = new Student(){StuId = 1006,StuName = "学员6"}; List<Student> stuList = new List<Student>(){obj1,obj2,obj3,obj4,obj5,obj6}; var count1 = (from c in stuList where c.StuId > 1010 select c).Count();//混合模式 var count2 = stuList .Where(c => c.stuId > 1010) .Count();//查询方法 //输出结果 Console.WriteLine("count1 = {0},count2 = {1}",count1,count2); Console.ReadLine(); } //求最大,最小,平均值,总数的方法与上面一致
- 排序类:ThenBy
//ThenBy复合排序 static void Main(string[] args) { var stus1 = from s in stuList orderby s.StuName,s.Age select s; var stus2 = stuList .OrderBy(s => s.StuName) .ThenBy(s => s.Age) .ThenBy(s => s.StuId) .Select(p => p); }
- 分区类:Take(提取指定数量的项),TakeWhile(只要满足指定条件,就会返回序列的元素,然后跳过剩余元素),Skip(跳过指定数量的项并获取剩余的项),SkipWhile(只要满足指定条件,就会跳过序列的元素,然后跳过剩余元素)
static void Main(string[] args) { int[] nums = {1,2,3,4,5,6,7,8,9}; var list1 = nums.Skip(1).Take(3);//结果为2,3,4 var list2 = nums.ShipWhile(i => i%3 != 0).TakeWhile(i => i%2 !=0);//结果为3 }
- 集合类:Distinct(去重)
static void Main(string[] args) { int[] nums = {1,2,6,5,6,2,7,8}; var list = nums.Distinct();//结果为1,2,6,5,7,8 }
- 生产类:Range(生成一个整数序列),Repeat(生成一个重复项的序列)
static void Main(string[] args) { var nums1 = Enumerable.Range(1,10);//结果为1-10 var nums2 = Enumerable.Repeat("LinQ best!",10);//结果为10个LinQ best! }
注意:
-
Range/Repeat不是扩展方法,而是普通的静态方法
-
Range只能产生整数序列
-
Repeat可以产生泛型序列
-
所有查询方法都放在System.Linq.Enumerable类中