(一)Linq基础
1)Linq解决的问题
- 面向对象编程语言与数据访问方法长期分离,以嵌入式的方式开发,例如:
- 编程语言中的数据类型与数据库中的数据类型形成两套体系,如:C#中的字符串string 在SQL中使用NVarchar/ Varchar/ Char来表示;
- SQL和XML都有各自的查询语言,而对象没有自己的查询语言,如要从List<>集合或者数组中找到符合要求的元素,非常困难;
2)什么是Linq
-
Linq(Language Integrated Query, 语言集成查询)
- 是微软公司提供的一项新技术,能够将查询功能直接引入到C#、VB.NET等编程语言中;
- 查询操作可以通过编程语言自身来表示,而不是嵌入字符串SQL语句;
-
Linq 主要包含以下三个部分:
- Linq to Objects 主要负责对象的查询;
- Linq to XML 主要负责XML的查询;
- Linq to ADO.NET主要负责数据库的查询:
- (1) Linq to SQL (目前已经没人使用)
- (2) Linq to DataSet
- (3) Linq to Entities(重点学)
-
Linq所在命名空间:
- System.Linq; 该命名空间已经由系统自动引入;
- 因此微软默认建议多使用Linq技术查询;
Linq的组成架构:
3)采用Linq技术与不采用Linq技术的对比
<1> 不使用Linq技术
using System;
using System.Collections.Generic;
namespace LinqDemo
{
class Program
{
//找出所有的奇数
static void Main(string[] args)
{
int[] nums = { 1, 7, 2, 6, 5, 4, 9, 13, 20 };
List<int> list = new List<int>();
foreach (var item in nums)
{
if (item % 2 == 1)
list.Add(item);
}
list.Sort();
list.Reverse();
foreach (var item in list)
{
Console.WriteLine(item);
}
}
}
}
<2> 使用Linq技术
查询的时候,可以采用优化的算法,提高查询的效率;
using System;
using System.Collections.Generic;
using System.Linq;
namespace LinqDemo
{
class Program
{
static void Main(string[] args)
{
int[] nums = { 1, 7, 2, 6, 5, 4, 9, 13, 20 };
var list = from num in nums
where num % 2 == 1
orderby num ascending
select num;
foreach (var item in list)
{
Console.WriteLine(item);
}
}
}
}
(二)Linq查询方法详解
1) 获取数据:扩展方法Select()
Select()是一个泛型扩展方法;
Select()方法使用的时候,要求传递一个委托实例(委托实例就是一个方法)
应用:
注意:数组、泛型集合都可以使用扩展方法Select();
using System;
using System.Collections.Generic;
using System.Linq;
namespace LinqDemo
{
class Program
{
static void Main(string[] args)
{
int[] nums = { 1, 9, 78, 55, 45, 23, 56, 89, 12, 32, 33 };
var list = nums.Select(item => item * item);
foreach (var item in list)
{
Console.WriteLine(item);
}
}
}
}
2)筛选数据:where() 方法
- where()方法是一个扩展泛型方法;
- where()方法使用的时候要求传递一个委托实例,但是该实例是一个判断条件,因此返回的类型必须是bool类型;
应用:
using System;
using System.Collections.Generic;
using System.Linq;
namespace LinqDemo
{
class Program
{
static void Main(string[] args)
{
int[] nums = { 1, 9, 78, 55, 45, 23, 56, 89, 12, 32, 33 };
var list = nums.Where(item => item % 2 == 1).Select(i => i * i);
foreach (var item in list)
{
Console.WriteLine(item);
}
}
}
}
3)排序数据:OrderBy()
- OrderBy()是一个扩展方法;
- OrderBy()里面的参数要求传递一个排序的字段,默认按照升序排序;
如果想降序排序,可以使用OrderByDescending方法;
应用:
using System;
using System.Collections.Generic;
using System.Linq;
namespace LinqDemo
{
class Program
{
static void Main(string[] args)
{
int[] nums = { 1, 9, 78, 55, 45, 23, 56, 89, 12, 32, 33 };
var list = nums.Where(item => item % 2 == 0).Select(item => item * 2).OrderBy(item => item);
foreach (var item in list)
{
Console.WriteLine(item);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
namespace LinqDemo
{
class Program
{
static void Main(string[] args)
{
string[] names = { "张三", "李四", "郭保彦", "刘宁", "上海", "北京" };
var list = names.Where(item => item.Length == 2).Select(item => item)
.OrderBy(item => item.Substring(0, 1));
foreach (var item in list)
{
Console.WriteLine(item);
}
}
}
}
4)分组数据:GroupBy()方法
- OrderBy()是一个扩展方法;
- OrderBy()里面的参数要求传递一个分组的字段
应用:
using System;
using System.Collections.Generic;
using System.Linq;
namespace LinqDemo
{
class Program
{
static void Main(string[] args)
{
string[] names = { "张三", "李四", "网三三", "李思思", "上海", "北京" };
var list = names.Select(item => item)
.OrderBy(item => item.Substring(0, 1)).GroupBy(item=>item.Length);
//遍历的时候需要注意,需要两层循环
foreach (var item in list)
{
Console.WriteLine("分组字段:{0}", item.Key);
foreach (var i in item)
{
Console.WriteLine(i);
}
}
}
}
}
(三)Linq查询时机与查询形式
1)查询步骤:
步骤:获取数据源、定义查询、执行查询;
结论:
2) 使用“聚合扩展方法”返回单一结果,强制查询立即执行
3)Linq查询的两种形式
<1> Method Syntax,查询方法方式
- 主要利用System.Linq.Enumerable类中定义的扩展方法和Lambda表达式方式进行查询;
- 在此之前所用的查询都是这种方法;
<2> Query Syntax,查询语句方式
- 一种更接近于SQL语法的查询方式,可读性更好;
- 查询语句最后还是要被翻译为查询方法;
注意:
(四)Linq查询子句
1)Linq查询子句概述
<1> 查询表达式
- 是一种用查询语法表示的表达式,有一组用于类似于SQL的语法编写的句子组成;
- 每个句子可以包含一个或者多个C#表达式;
<2> Linq查询表达式包含的子句:
- from子句:指定查询操作的数据源和范围变量;
- where子句:筛选元素的逻辑条件,返回值是一个bool类型;
- select子句:指定查询结果的类型和表现形式;
- orderby子句:对查询结果进行排序;
- group子句:对查询结果进行分组;
- into子句:提供一个临时标识符,该表示可充当对 join/ group/ select 子句结果的引用;
- join子句:连接多个查询操作的数据源;
- let子句:引入用于存储查询表达式中的子表达式结果的范围变量;
2)from子句
<1> 概述:
- Linq查询表达式必须包含from子句,并且作为开头;
- from子句指定的数据源必须为IEnumerable、Ienumerable<>或者两者的派生类型(例如:数组、集合List<>、ArrayList等)
<2> 数据源
- 如果数据源是泛型类型,则编译器可以自动推断出范围变量的类型,比如上面的num类型为int;
- 如果数据源为非泛型类型,如ArrayList,则必须显式地指定范围变量的数据类型;
<3> 复合from子句查询
如果数据源(本身是一个序列)的元素还包含子数据源(如序列、列表等,如果要查询子数据源的元素),则需要使用复合from子句;
using System;
using System.Collections.Generic;
using System.Linq;
namespace LinqDemo
{
class Program
{
static void Main(string[] args)
{
Student obj1 = new Student()
{
StudentId = 101,
StudentName = "zhansan",
ScoreList = new List<int>() { 78, 89, 99}
};
Student obj2 = new Student()
{
StudentId = 102,
StudentName = "LiuNing",
ScoreList = new List<int>() { 88, 66, 78 }
};
Student obj3 = new Student()
{
StudentId = 103,
StudentName = "guo",
ScoreList = new List<int>() { 100, 89, 78 }
};
//将学员封装到集合中
List<Student> stuList = new List<Student>() { obj1, obj2, obj3 };
//查询95分以上的学员
var result = from stu in stuList
from score in stu.ScoreList
where score >= 95
select stu;
//显示查询结果
foreach (var item in result)
{
Console.WriteLine(item.StudentName);
}
}
}
}
<4> 多个from子句查询
若Linq查询表达式包含两个或两个以上的独立数据源时,可以使用多个from子句查询所有数据源中的数据;
using System;
using System.Collections.Generic;
using System.Linq;
namespace LinqDemo
{
class Program
{
static void Main(string[] args)
{
Student obj1 = new Student()
{
StudentId = 117,
StudentName = "zhansan",
};
Student obj2 = new Student()
{
StudentId = 102,
StudentName = "LiuNing",
};
Student obj3 = new Student()
{
StudentId = 103,
StudentName = "guo",
};
Student obj4 = new Student()
{
StudentId = 104,
StudentName = "liu",
};
Student obj5 = new Student()
{
StudentId = 105,
StudentName = "yi",
};
Student obj6 = new Student()
{
StudentId = 115,
StudentName = "tian",
};
//将学员封装到集合中
List<Student> stuList1 = new List<Student>() { obj1, obj2, obj3 };
List<Student> stuList2 = new List<Student>() { obj4, obj5, obj6 };
//查询95分以上的学员
var result = from stu1 in stuList1
where stu1.StudentId > 110
from stu2 in stuList2
where stu2.StudentId > 110
select new { stu1, stu2 };
//显示查询结果
foreach (var item in result)
{
Console.WriteLine(item.stu1.StudentName);
Console.WriteLine(item.stu2.StudentName);
}
}
}
}
3)其他子句
(五)Linq高级查询
1)Count返回集合项的数目
using System;
using System.Collections.Generic;
using System.Linq;
namespace LinqDemo
{
class Program
{
static void Main(string[] args)
{
Student obj1 = new Student()
{
StudentId = 117,
StudentName = "zhansan",
};
Student obj2 = new Student()
{
StudentId = 102,
StudentName = "LiuNing",
};
Student obj3 = new Student()
{
StudentId = 103,
StudentName = "guo",
};
Student obj4 = new Student()
{
StudentId = 104,
StudentName = "liu",
};
Student obj5 = new Student()
{
StudentId = 105,
StudentName = "yi",
};
Student obj6 = new Student()
{
StudentId = 115,
StudentName = "tian",
};
//将学员封装到集合中
List<Student> stuList1 = new List<Student>() { obj1, obj2, obj3, obj4, obj5, obj6 };
//查询
var result = (from stu1 in stuList1
where stu1.StudentId > 102
select stu1).Count();
var result1 = stuList1.Where(item => item.StudentId > 102).Count();
//显示查询结果
Console.WriteLine(result);
Console.WriteLine(result1);
}
}
}
2)Max/Min/Average/Sum【数学上】
3)排序类
ThenBy提供复合排序条件;
using System;
using System.Collections.Generic;
using System.Linq;
namespace LinqDemo
{
class Program
{
static void Main(string[] args)
{
Student obj1 = new Student()
{
StudentId = 117,
StudentName = "zhansan",
Age = 16
};
Student obj2 = new Student()
{
StudentId = 102,
StudentName = "LiuNing",
Age = 18
};
Student obj3 = new Student()
{
StudentId = 103,
StudentName = "guo",
Age = 21
};
Student obj4 = new Student()
{
StudentId = 104,
StudentName = "liu",
Age = 89
};
Student obj5 = new Student()
{
StudentId = 105,
StudentName = "yi",
Age = 78
};
Student obj6 = new Student()
{
StudentId = 115,
StudentName = "tian",
Age = 56
};
//将学员封装到集合中
List<Student> stuList1 = new List<Student>() { obj1, obj2, obj3, obj4, obj5, obj6 };
//查询
var result = from s in stuList1
orderby s.Age, s.StudentName
select s;
var result1 = stuList1.OrderBy(item => item.Age).ThenBy(item => item.StudentName).Select(item => item);
//显示查询结果
foreach (var item in result)
{
Console.WriteLine(item.StudentName);
}
Console.WriteLine("------------------");
foreach (var item in result1)
{
Console.WriteLine(item.StudentName);
}
}
}
}
4)分区类查询
- Take 提取支墩数量的项;
- Skip 跳过指定数量的项并获取剩余的项;
- TakeWhile 只要满足指定条件,就会返回序列的元素,然后跳过剩余的元素;
- SkipWhile 只要满足指定的条件,就跳过序列中的元素,然后返回剩余元素;
using System;
using System.Collections.Generic;
using System.Linq;
namespace LinqDemo
{
class Program
{
static void Main(string[] args)
{
int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var list = nums.SkipWhile(i => i % 3 != 0).TakeWhile(i => i % 2 != 0).Select(item => item);
foreach (var item in list)
{
Console.WriteLine(item);
}
}
}
}
5)集合类查询Distinct
Distinct 去掉集合中的重复项;
using System;
using System.Collections.Generic;
using System.Linq;
namespace LinqDemo
{
class Program
{
static void Main(string[] args)
{
int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9 };
var list = nums.Distinct();
foreach (var item in list)
{
Console.WriteLine(item);
}
}
}
}
6) 生成类查询
- Range生成一个整数序列;
Repeat生成一个重复项的序列;
注意问题:
- Range/Repeat不是扩展方法,而是普通的静态方法;
- Range 只能产生整数序列;
- Repeat可以产生泛型序列;
- 所有的查询方法都存放在System.Linq.Enumerable静态类中;