概念
是.NET框架的扩展,它允许我们以使用SQL查询数据库的方式来查询数据集合
使用LINQ,你可以从数据库、程序对象的集合以及XML文档中查询数据。
例
查询小于10的元素
static void Main(string[] args)
{
int[] number = { 2, 15, 5, 15 };
IEnumerable<int> lowNums = from n in number
where n < 10
select n;
foreach (var x in lowNums)
Console.Write($"{x},");
Console.ReadKey();
}
结果
2,5,
分析
from-in查询语句开头,很像foreach-in。 from 子句中引用的数据源必须具有 IEnumerable、IEnumerable
类型之一,或 IQueryable 等派生类型
where子句用在查询表达式中,用于指定将在查询表达式中返回数据源中的哪些元素
select子句指定在执行查询时产生的值的类型
匿名类型
匿名类型经常用于LINQ查询
匿名函数与对象初始化语句相似,但是没有类名和构造函数,无需关注初始化语句是否声明
语法
//没有类名 FieldOrProp = InitExpr初始语句
new {FieldOrProp = InitExpr, FieldOrProp = InitExpr...}
//对象初始化语句
new TypeName {FieldOrProp = InitExpr,FieldOrProp = InitExpr...}
实例
static void Main(string[] args)
{
var student = new { Name = "Marg Jones", Age = 19, Maior = "History" };
Console.WriteLine($"stdent. Name:{student.Name},student.Age:{student.Age},student.Maior:{student.Maior}");
Console.ReadKey();
}
结果
stdent. Name:Marg Jones,student.Age:19,student.Maior:History
分析
可以看到Name,Age等无需先声明
类型是var类型
规则
- 匿名类型只能和局部变量配合使用,不能用于类成员。
- 由于匿名类型没有名字,我们必须使用var关键字作为变量类型。
- 不能设置匿名类型对象的属性。编译器为匿名类型创建的属性是只读的。
其它赋值形式
- 简单标识符
- 成员访问表达式
实例
class Other
{
static public string Name = "Mary Jones";
}
class Program
{
static void Main(string[] args)
{
string Major = "History";
var student = new { Age = 19, Other.Name, Major };
Console.WriteLine($"student.Age:{student.Age},student.Other.Name:{student.Age},student.Major:{student.Major}");
Console.ReadKey();
}
}
结果
student.Age:19,student.Other. Name:19,student.Major:History
分析
Age赋值形式,Other.Name成员访问,Major标识符
方法查询和查询语句
LINQ查询时,使用的两种形式语法:查询语法和方法语法
- 方法语法:使用标准的方法调用。命令式,需执行方法调用顺序
- 查询语法:和SQL语句相似,声明式,无需指明如何查询,微软推荐
实例
static void Main(string[] args)
{
int[] numbers = { 2, 5, 28, 31, 17, 16, 42 };
var numsQuery = from n in numbers //查询语法
where n < 20
select n;
var numsMethod = numbers.Where(x => x < 20); //方法语法
int numsCount = (from n in numbers //两种形式结合
where n < 20
select n).Count();
Console.Write("numsQuery:");
foreach (var x in numsQuery)
Console.Write($"{x},");
Console.WriteLine();
Console.Write("numsMethod,");
foreach (var x in numsMethod)
Console.Write($"{x},");
Console.WriteLine();
Console.WriteLine(numsCount);
Console.ReadKey();
}
结果
numsQuery:2,5,17,16,
numsMethod,2,5,17,16,
4
查询常量
KINQ查询返回两种类型的结果
- 可枚举类型:它满足查询参数的项列表
- 标量的单一值:它是满足查询条件的结果的某种摘要形式
实例
static void Main(string[] args)
{
int[] numbers = { 2, 5, 28, 31, 17, 16, 42 };
IEnumerable<int> lowNums = from n in numbers //返回可枚举类型
where n < 20
select n;
int numsCount = (from n in numbers //返回整数
where n < 20
select n).Count();
Console.ReadKey();
}
总结
- 如果查询表达式返回枚举,查询一直到处理枚举时才会执行。
- 如果枚举被处理多次,查询就会执行多次。
- 如果在进行遍历之后,查询执行之前数据有改动,则查询会使用新的数据。
- 如果查询表达式返回标量,查询立即执行,并且把结果保存在查询变量中。
查询表达式的结构
由from子句组成
规则
- 子句必须按照一定的顺序出现。
- from子句和select...group子句这两部分是必需的。
- 其他子句是可选的。
- 在LINQ查询表达式中,select子句在表达式最后。这与SQL的SELECT语句在查询的开始处不一样。
- 可以有任意多的from...let...where子句,
from子句
指定要作为数据源使用的数据集合
规则
迭代变量逐个表示数据源的每一个元素。
语法
from Type Item in Items
- Type是集合中元素的类型。这是可选的,因为编译器可以从集合来推断类型。
- Item是迭代变量的名字。
- Items是要查询的集合的名字。集合必须是可枚举的(IEnumerable)
例
int[] arr1 = {10, 11, 12, 13};
var query = from item in arr1 //item迭代变量
where item < 13
select item;
foreach(var item in query)
Console.Write($"{item}, ");
结果
10, 11, 12
与foreach不同
- foreach语句命令式地指定了要从第一个到最后一个按顺序地访问集合中的项。而from子句则声明式地规定集合中的每个项都要被访问,但并没有假定以什么样的顺序。
- foreach语句在遇到代码时就执行其主体,而from子句什么也不执行。它创建可以执行查询的后台代码对象。只有在程序的控制流遇到访问查询变量的语句时,才会执行查询。
join子句
- 使用联结来结合两个或更多集合中的数据。
- 联结操作接受两个集合然后创建一个临时的对象集合,每一个对象包含原始集合对象中的所有字段。
语法
指定第二个集合要和之前子句中的集合进行连结
必须使用equals来进行比较,不能用==运算符
//Identifier in Collection2 指定另外的集合和ID引用它
//equals用于比较相等性的字段
join Identifier in Collection2 on Field1 equals Field2
例
var query = from s in students
join c in studentsInCourses on s.StID equals c.StID
实例
有3个学生和3门课程,学生参加不同的课程,查找参加历史课的学生
//包含学生的姓氏和学号
public class Student
{
public int StID;
public string LastName;
}
//参与课程的学生,包含课程名和学生ID
public class CourseStudent
{
public string CourseName;
public int StID;
}
static Student[] students = new Student[]
{
new Student{StID = 1, LastName="Carson"},
new Student{StID = 2, LastName="Klassen"},
new Student{StID = 3, LastName="Fleming"},
};
static CourseStudent[] studentsInCourse = new CourseStudent[]
{
new CourseStudent{CourseName = "Art", StID = 1},
new CourseStudent{CourseName = "Art", StID = 2},
new CourseStudent{CourseName = "History", StID = 1},
new CourseStudent{CourseName = "History", StID = 3},
new CourseStudent{CourseName = "Physics", StID = 3},
};
static void Main(string[] args)
{
//查询所有选择了历史课的学生的姓氏
var query = from s in students
join c in studentsInCourse on s.StID equals c.StID
where c.CourseName == "History"
select s.LastName;
foreach (var q in query)
Console.WriteLine($"选择历史课的学生:{q},");
Console.ReadKey();
}
结果
选择历史课的学生:Carson,
选择历史课的学生:Fleming,
查询主体中的片段
from子句
用法和上面一样
实例
var groupA = new[] {3, 4, 5, 6};
var groupB = new[] {6, 7, 8, 9};
var someInts = from a in groupA //必需的第一个from子句
from b in groupB //查询主体的第一个子句
where a > 4 && b <= 8
select new {a, b, sum = a + b}; //匿名类型对象
foreach(var a in someInts)
Console.WriteLine(a);
结果
let子句
let子句接受一个表达式的运算并且把它赋值给一个需要在其他运算中使用的标识符
语法
let Identifier = Expression
实例
查询表达式将数组groupA中的每一个成员与数组groupB中的每一个成员进行配对
var groupA = new[] {3, 4, 5, 6};
var groupB = new[] {6, 7, 8, 9};
var someInts = from a in groupA //必需的第一个from子句
from b in groupB //查询主体的第一个子句
let sum = a + b
where sum == 12
select new {a, b, sum}; //匿名类型对象
foreach(var a in someInts)
Console.WriteLine(a);
结果
where子句
where子句根据之后的运算来去除不符合指定条件的项
规则
- 只要是在from.. .let. . .where部分中,查询表达式可以有任意多个where子句。
- 一个项必须满足where子句才能避免在之后被过滤。
注意
- 只要是在from...let. . .where部分中,查询表达式可以有任意多个where子句。
- 一个项必须满足where子句才能避免在之后被过滤。
实例
var groupA = new[] { 3, 4, 5, 6 };
var groupB = new[] { 6, 7, 8, 9 };
var someInts = from a in groupA //必需的第一个from子句
from b in groupB //查询主体的第一个子句
let sum = a + b
where sum >= 11 //条件1
where a == 4 //条件2
select new { a, b, sum }; //匿名类型对象
foreach (var a in someInts)
Console.WriteLine(a);
结果
orderby子句
orderby子句接受一个表达式并根据表达式按顺序返回结果项。
语法
ascending
和descending
设置排序方式,可选
orderby ascending/descending
规则
- orderby子句的默认排序是升序。然而,我们可以使用ascending和descending关键字显式地设置元素的排序为升序或降序。
- 可以有任意多个子句,它们必须使用逗号分隔。
实例
var students = new[]
{
new {LName="Jones", FName="Mary", Age=19, Major="History"},
new {LName="Smith", FName="Bob", Age=20, Major="CompSci"},
new {LName="Fleming", FName="Carol", Age=21, Major="History"}
};
var query = from student in students
orderby student.Age //根据Age排序
select student;
foreach(var s in query)
{
Console.WriteLine($"s.LName:{s.LName},s.FName:{s.FName},s.Age:{s.Age},s.Major:{s.Major}");
}
结果
s.LName:Jones,s.FName:Mary,s.Age:19,s.Major:History
s.LName:Smith,s.FName:Bob,s.Age:20,s.Major:CompSci
s.LName:Fleming,s.FName:Carol,s.Age:21,s.Major:History
select...group子句
有两种类型的子句组成select...group部分——select子句和group...by子句
功能
- select子句指定所选对象的哪部分应该被select。它可以指定
- 整个数据项。
- 数据项的一个字段。
- 数据项中几个字段组成的新对象(或类似其他值)。
- group...by子句是可选的,用来指定选择的项如何被分组
语法
select Expression
--------------------
group Expression1 by Expression2
实例
var students = new[]
{
new {LName="Jones", FName="Mary", Age=19, Major="History"},
new {LName="Smith", FName="Bob", Age=20, Major="CompSci"},
new {LName="Fleming", FName="Carol", Age=21, Major="History"}
};
var query = from s in students
select s;
foreach(var s in query)
{
Console.WriteLine($"s.LName:{s.LName},s.FName:{s.FName},s.Age:{s.Age},s.Major:{s.Major}");
}
或者查询LName
var query = from s in students
select s.LName;
foreach(var s in query)
{
Console.WriteLine($"s.LName:{s.LName},s.FName:{s.FName},s.Age:{s.Age},s.Major:{s.Major}");
}
查询匿名类型
var students = new[]
{
new {LName="Jones", FName="Mary", Age=19, Major="History"},
new {LName="Smith", FName="Bob", Age=20, Major="CompSci"},
new {LName="Fleming", FName="Carol", Age=21, Major="History"}
};
var query = from s in students
select new { s.LName, s.FName, s.Age, s.Major};
foreach(var s in query)
{
Console.WriteLine($"s.LName:{s.LName},s.FName:{s.FName},s.Age:{s.Age},s.Major:{s.Major}");
}
group子句
group子句把select的对象根据一些标准进行分组
注意
- 如果项包含在查询的结果中,它们就可以根据某个字段的值进行分组。作为分组依据的属性叫做键(key)。
- group子句返回的不是原始数据源中项的枚举,而是返回可以枚举已经形成的项的分组的可枚举类型。
- 分组本身是可枚举类型,它们可以枚举实际的项。
实例
var students = new[]
{
new {LName="Jones", FName="Mary", Age=19, Major="History"},
new {LName="Smith", FName="Bob", Age=20, Major="CompSci"},
new {LName="Fleming", FName="Carol", Age=21, Major="History"}
};
var query = from student in students
group student by student.Major;
foreach(var s in query)
{
Console.WriteLine($"s.Key:{s.Key}");
foreach (var t in s)
{
Console.WriteLine($"t.LName:{t.LName},t.FName:{t.FName}");
}
}
结果
s.Key:History
t.LName:Jones,t.FName:Mary
t.LName:Fleming,t.FName:Carol
s.Key:CompSci
t.LName:Smith,t.FName:Bob
查询延续:into子句
查询延续子句可以接受查询的一部分结果并赋予一个名字,从而可以在查询的另一部分中使用。
语法
join Type Identifier in Expression
on Expression equals Expression
into Identifier
实例
var groupA = new[] { 3, 4, 5, 6 };
var groupB = new[] { 4, 5, 6, 7 };
var someInts = from a in groupA
join b in groupB on a equals b
into groupAandB //查询延续
from c in groupAandB
select c;
foreach (var a in someInts)
Console.Write(a);
结果
4,5,6,