LINQ(Language Integrated Query)读作[lɪŋk]
,是一种用于.Net平台的查询语言集成技术,它能够允许开发人员用类似SQL结构化查询的语法来查询和操作数据源,这里的数据源包括对象集合、XML文档、数据库等。LINQ支持C#和VB.NET,C#中使用LINQ的主要目的是简化查询数据逻辑的写法,提高代码的可读性和可维护性。
下面我们直接看一个LINQ的使用例子,代码中我们查询长度为4的所有字符串。
List<string> data = new List<string> { "Tom", "Jerry", "Kate", "Lucy", "Bob" };
// 使用LINQ查询长度为4的字符串
IEnumerable<string> result = from s in data where s.Length == 4 select s;
// 输出查询结果
foreach (var s in result)
{
Console.WriteLine(s);
}
有人可能觉得上面例子其实不用LINQ也很容易实现,毕竟迭代判断也不复杂。实际上,LINQ其实体现了一种结构化查询的思想,这和在关系型数据库中,我们使用SQL语言的理由是一样的。如果集合元素是个复杂的对象,而且有多个集合「关联查询」的情况,LINQ的优势就体现出来了。
我们能够使用LINQ从某个数据源中查询数据,其实是被查询的数据源将自身实现为了LINQ提供者程序(LINQ Provider)。.Net Framework和.Net Core中提供了一些默认的LINQ Provider,常见的包括:
LINQ To Objects:最常用的LINQ Provider之一,用于从对象集合中查询数据。
LINQ To XML:用于使用LINQ语法查询和操作XML文档。
LINQ To SQL:用于和关系型数据库交互,允许开发者用LINQ语法来查询数据库中的数据,后来该LINQ Provider被EFCore框架取代。
除此之外,我们也可以实现自定义的LINQ Provider。
LINQ在C#中有两种主要的语法风格:方法语法和查询语法。
方法语法使用类似于函数调用的方式来执行LINQ查询,代码中我们需要使用一系列LINQ扩展方法来构建和链式组合查询操作,下面是使用方法语法查询数据的例子。
List<string> data = new List<string> { "Tom", "Jerry", "Kate", "Lucy", "Bob" };
IEnumerable<string> result = data.Where(s => s.Length == 4);
查询语法更像完整的SQL语句,我们需要在LINQ中使用from
、where
、select
等关键字描述查询操作,下面是使用查询语法查询数据的例子。
List<string> data = new List<string> { "Tom", "Jerry", "Kate", "Lucy", "Bob" };
IEnumerable<string> result = from s in data where s.Length == 4 select s;
上面例子中,方法语法和查询语法是等效的,无论使用哪种风格都可以达到相同的查询目的。方法语法通常更紧凑,适合链式操作;而查询语法更类似SQL语句,对于某些人来说可读性更好。实际开发中,我们应该根据实际情况进行选择。
LINQ查询可能返回不同类型的查询结果,这取决于具体的查询操作和想要呈现结果的方式(投影)。
IEnumerableAge
属性等于18
的对象集合。
List<Student> data = new List<Student>
{
new Student { Name = "Tom", Age = 18 },
new Student { Name = "Lucy", Age = 17 },
new Student { Name = "Bob", Age = 18 }
};
IEnumerable<Student> result = from s in data where s.Age == 18 select s;
匿名类型:LINQ查询可返回匿名类型,这是一种临时的没有明确类名的类型,通常用于临时投影查询结果。
List<Student> data = new List<Student>
{
new Student { Name = "Tom", Age = 18 },
new Student { Name = "Lucy", Age = 17 },
new Student { Name = "Bob", Age = 18 }
};
var result = from s in data where s.Age == 18 select new { Name = s.Name, Age = s.Age };
注意:匿名类型变量由于没有具体的类型名,我们通常使用var
声明。
单个值类型:对于一些聚合查询或是查询单个元素等情况,LINQ可能会返回单个值而不是集合。
List<Student> data = new List<Student>
{
new Student { Name = "Tom", Age = 18 },
new Student { Name = "Lucy", Age = 17 },
new Student { Name = "Bob", Age = 18 }
};
double avg = data.Average(s => s.Age);
from
子句指定了作为数据源使用的数据集合和迭代变量。
from Type d in data
其中的Type
类型可以省略不写,实际开发中一般都将其省略。from
子句可以有任意个,等价为多层循环,下面是一个例子。
List<int> data1 = new List<int> { 1, 2, 3, 4, 5, 6, 7 };
List<int> data2 = new List<int> { 2, 4, 6, 8, 10 };
var result = from d1 in data1
from d2 in data2
where d1 > 3 && d2 > 6
select new { D1 = d1, D2 = d2 };
上面代码的计算结果为:
{ D1 = 4, D2 = 8 }
{ D1 = 4, D2 = 10 }
{ D1 = 5, D2 = 8 }
{ D1 = 5, D2 = 10 }
{ D1 = 6, D2 = 8 }
{ D1 = 6, D2 = 10 }
{ D1 = 7, D2 = 8 }
{ D1 = 7, D2 = 10 }
join
子句用于连接两个集合创建一个新的集合,from
子句后可以有任意个join
子句来生成连接集合。
from Type d1 in data1 join Type d2 in data2 on join条件
类似from
子句,其中的Type
类型都可以省略不写。
List<Student> students = new List<Student>
{
new Student { Name = "Tom", Age = 18, CourseId = 1 },
new Student { Name = "Lucy", Age = 17, CourseId = 1 },
new Student { Name = "Bob", Age = 18, CourseId = 2 }
};
List<Course> courses = new List<Course>()
{
new Course() { CourseId = 1, Name = "数学" },
new Course() { CourseId = 2, Name = "语文" }
};
IEnumerable<Student> result = from student in students
join course in courses on student.CourseId equals course.CourseId
where course.Name == "数学"
select student;
let
子句接收一个运算表达式并将其赋值给一个标识符,下面是一个例子。
List<int> data1 = new List<int> { 1, 2, 3, 4, 5, 6, 7 };
List<int> data2 = new List<int> { 2, 4, 6, 8, 10 };
IEnumerable<int> result = from d1 in data1
from d2 in data2
let sum = d1 + d2
where sum > 5
select sum;
where
子句需要指定筛选条件,LINQ根据该条件来筛选结果数据。where
子句可以有多个,它们是AND关系,然而我们一般不会这样写,而是在一个where
子句中使用&&
连接多个条件。
上面代码中我们已经使用过很多次where
子句了,这里就不单独编写例子了。
orderby
子句用于对查询结果进行排序。
List<int> data = new List<int> { 7, 6, 5, 4, 3, 2, 1 };
IEnumerable<int> result = from d in data orderby d ascending select d;
orderby
子句中我们可以使用ascending
和descending
指定升序或降序。
groupby
子句用于对查询结果进行分组,它可以看作一个包含分组功能的select
子句。
List<Student> students = new List<Student>
{
new Student { Name = "Tom", Age = 18, CourseId = 1 },
new Student { Name = "Lucy", Age = 17, CourseId = 1 },
new Student { Name = "Bob", Age = 18, CourseId = 2 }
};
IEnumerable<IGrouping<int, Student>> result = from student in students
group student by student.CourseId;
foreach (var group in result)
{
foreach (var d in group)
{
Console.WriteLine("{0} {1} {2}", group.Key, d.Name, d.Age);
}
}
上面代码的计算结果为:
1 Tom 18
1 Lucy 17
2 Bob 18