导航
什么是LINQ
语言集成查询 (LINQ)用于 .NET Framework 集合、SQL Server 数据库、ADO.NET 数据集和 XML 文档的查询,提供简洁、方便的查询操作。 在 LINQ 查询中,始终会用到对象。 可以使用相同的基本编码模式来查询和转换 XML 文档、SQL 数据库、ADO.NET 数据集、.NET 集合中的数据以及对其有 LINQ 提供程序可用的任何其他格式的数据。
LINQ 需要IEnumerable 或 IEnumerable<T>。支持 IEnumerable<T> 或派生接口 (如泛型 IQueryable<T> 的类型称为"可查询 类型"。
在LINQ查询表达式包含三个子句:from、where 和 select。 from, where and select. (If you are familiar with SQL, you will have noticed that the ordering of the clauses is reversed from the order in SQL.) The from clause specifies the data source, the where clause applies the filter, and the select clause specifies the type of the returned elements.‘ data-guid="afd248b5dd5f746a05bbeaa52f759fb0">(如果您熟悉 SQL,您会注意到这些子句的顺序与 SQL 中的顺序相反。)from子句指定数据源,where 子句应用筛选器,select 子句指定返回的元素的类型。
延迟执行:查询变量本身只是存储查询命令。 foreach statement.‘ data-guid="4dd742ca821a9d66a23c6f91087594f3">实际的查询执行会延迟到在 foreach 语句中循环访问查询变量时发生。
foreach statement.‘ data-guid="4dd742ca821a9d66a23c6f91087594f3"> 强制立即执行:对一系列源元素执行聚合函数的查询必须首先循环访问这些元素。 Count、Max、Average 和 First 就属于此类查询。 foreach statement because the query itself must use foreach in order to return a result.‘ data-guid="a727cccb9f42d321797224ee7bade4ff">由于查询本身必须使用 foreach 以便返回结果,因此这些查询在执行时不使用显式 foreach 语句。 另外还要注意,这些类型的查询L返回单个值,而不是 IEnumerable 集合。在延迟执行查询之中,若要强制立即执行任意查询并缓存其结果,可以调用 ToList<TSource> 或 ToArray<TSource> 方法
基本 LINQ 查询操作
from:
在 LINQ 查询中,第一步是指定数据源。 像在大多数编程语言中一样,在 C# 中,必须先声明变量,才能使用它。 from clause comes first in order to introduce the data source (customers) and the range variable (cust).‘ data-guid="03049c431f2ec186e44c1fd289b93418">在 LINQ 查询中,最先使用 from 子句的目的是引入数据源 (customers) 和范围变量 (cust)。
var queryAllCustomers = from cust in customers select cust;
where:
也许最常用的查询操作是应用布尔表达式形式的筛选器(where)。 此筛选器使查询只返回那些表达式结果为 true
的元素。 where clause.‘
data-guid="f60c2d0661233e41e7fae05d53ee126a">使用 where 子句生成结果。 实际上,筛选器指定从源序列中排除哪些元素。 customers who have an address in London are returned.‘
data-guid="3b5f7b8d54ae28cf07c0af8b36593d27">
var queryLondonCustomers = from cust in customers where cust.City == "London" select cust;
当然,也会有多条件查询,那么就需要用到“AND”或者“OR”
where cust.City=="London" && cust.Name == "Devon"
where cust.City == "London" || cust.City == "Paris"
orderby clause will cause the elements in the returned sequence to be sorted according to the default comparer for the type being sorted.‘ data-guid="ebc67d5253831281973fb07fd03758ff">orderby:
orderby clause will cause the elements in the returned sequence to be sorted according to the default comparer for the type being sorted.‘ data-guid="ebc67d5253831281973fb07fd03758ff">子句将使返回的序列中的元素按照被排序的类型的默认比较器进行排序。 Name property.‘ data-guid="b5985e2ca3b28f13996e65187c9ea5c9">例如,下面的查询可以扩展为按 Name 属性对结果进行排序。 Name is a string, the default comparer performs an alphabetical sort from A to Z.‘ data-guid="a471e75aff2720894aae8e6d8d9448ed">因为 Name 是一个字符串,所以默认比较器执行从 A 到 Z 的字母排序。若要按相反顺序(从 Z 到 A)对结果进行排序,请使用 orderby…descending 子句。
var queryLondonCustomers3 = from cust in customers where cust.City == "London" orderby cust.Name ascending select cust;
group:
group clause enables you to group your results based on a key that you specify.‘ data-guid="aa78cab9efa4b980a380042619ab2b4b">使用 group 子句,您可以按指定的键分组结果。 City so that all customers from London or Paris are in individual groups.‘ data-guid="d2783d974daf9ee0cb8d19fcea4b2f37">例如,您可以指定结果应按 City 分组,以便位于伦敦或巴黎的所有客户位于各自组中。 cust.City is the key.‘ data-guid="7d05cdf79bca463bb782df44bf6a2322">在本例中,cust.City 是键。
var queryCustomersByCity = from cust in customers group cust by cust.City;
group clause returns a sequence of IGrouping<TKey, TElement> objects that contain zero or more items that match the key value for the group.‘ data-guid="4272a88b0df2eae226b3df896d45caec">group 子句返回一个 IGrouping<TKey, TElement> 对象序列,这些对象包含零个或更多个与该组的键值匹配的项。 例如,可以按照每个字符串中的第一个字母对字符串序列进行分组。 char, and is stored in the Key property of each IGrouping<TKey, TElement> object.‘ data-guid="12ccab74ce9b2cd7372924064288a11c">在这种情况下,第一个字母是键且具有 char 类型,并且存储在每个 IGrouping<TKey, TElement> 对象的 Key 属性中。 编译器可推断该键的类型。
// Query variable is an IEnumerable<IGrouping<char, Student>> var studentQuery1 = from student in students group student by student.Last[0];
into contextual keyword.‘ data-guid="97a2f76eb269adf16cf627b1b29926e3">into:
into contextual keyword.‘ data-guid="97a2f76eb269adf16cf627b1b29926e3">如果您想要对每个组执行附加查询操作,则可以使用 into 上下文关键字指定一个临时标识符。 into, you must continue with the query, and eventually end it with either a select statement or another group clause, as shown in the following excerpt:‘ data-guid="dc227f40715517ea368c8180a803c5a2">使用 into 时,必须继续编写该查询,并最终用一个 select 语句或另一个 group 子句结束该查询,如下面的代码摘录所示:
var studentQuery = from student in students let avg = (int)student.Scores.Average() group student by (avg == 0 ? 0 : avg / 10) into g orderby g.Key select g;
当您想要按照多个键对元素进行分组时,可使用复合键。 通过使用匿名类型或命名类型来存储键元素,可以创建复合键。 Person has been declared with members named surname and city.‘ data-guid="8e83facbc1621b24a461010553fb6378">在下面的示例中,假定已经使用名为 surname 和 city 的两个成员声明了类 Person。 group clause causes a separate group to be created for each set of persons with the same last name and the same city.‘ data-guid="5046a52887f3cff859c7e3ea259e25d7">group 子句使得为每组具有相同姓氏和相同城市的人员创建一个单独的组。
group person by new {name = person.surname, city = person.city};
let:
在查询表达式中,存储子表达式的结果有时很有用,这样可以在随后的子句中使用。 let keyword, which creates a new range variable and initializes it with the result of the expression you supply.‘ data-guid="7ca679cd263f5cc253e3010373fd311d">可以使用 let 关键字完成这一工作,该关键字可以创建一个新的范围变量,并且用您提供的表达式的结果初始化该变量。 一旦用值初始化了该范围变量,它就不能用于存储其他值。 但如果该范围变量存储的是可查询的类型,则可以对其进行查询。
join:
联接运算创建数据源中没有显式建模的序列之间的关联。 例如,您可以执行联接来查找位于同一地点的所有客户和经销商。 join clause always works against object collections instead of database tables directly.‘
data-guid="037e62f2ab61f6b8fad40b13a19c77f8">在 LINQ 中,join 子句始终针对对象集合而非直接针对数据库表运行。
var innerJoinQuery = from cust in customers join dist in distributors on cust.City equals dist.City select new { CustomerName = cust.Name, DistributorName= dist.Name };
join clause is useful for associating elements from different source sequences that have no direct relationship in the object model.‘ data-guid="ab0596a91afde74d147c265044f7a154">使用 join 子句可以将来自不同源序列并且在对象模型中没有直接关系的元素相关联。 唯一的要求是每个源中的元素需要共享某个可以进行比较以判断是否相等的值。 例如,食品经销商可能具有某种产品的供应商列表以及买主列表。 join clause can be used, for example, to create a list of the suppliers and buyers of that product who are all in the same specified region.‘ data-guid="b9e076cc28901a64e87124d8d654abde">例如,可以使用 join 子句创建该产品同一指定地区供应商和买主的列表。
join clause can be used, for example, to create a list of the suppliers and buyers of that product who are all in the same specified region.‘ data-guid="b9e076cc28901a64e87124d8d654abde">内部连接:下面的示例演示一个简单的内部同等联接。 此查询产生一个“产品名称/类别”对平面序列。 同一类别字符串将出现在多个元素中。 categories has no matching products, that category will not appear in the results.‘ data-guid="8b97f8fbfa10965b3a67daee3e315f3b">如果 categories 中的某个元素不具有匹配的 products,则该类别不会出现在结果中。
var query = from person in people join cat in cats on person equals cat.Owner join dog in dogs on new { Owner = person, Letter = cat.Name.Substring(0, 1) } equals new { dog.Owner, Letter = dog.Name.Substring(0, 1) } select new { CatName = cat.Name, DogName = dog.Name };
分组联接:含有 into 表达式的 join 子句称为分组联接。分组联接会产生一个分层的结果序列,该序列将左侧源序列中的元素与右侧源序列中的一个或多个匹配元素相关联。 分组联接没有等效的关系术语;它本质上是一个对象数组序列。join clause will produce an empty array for that item.‘ data-guid="758d95913d1a3db36a5ebb5d88c30dd6">如果在右侧源序列中找不到与左侧源中的元素相匹配的元素,则 join 子句会为该项产生一个空数组。 因此,分组联接基本上仍然是一种内部同等联接,区别只在于分组联接将结果序列组织为多个组。如果您只选择分组联接的结果,则可以访问各个项,但无法识别结果所匹配的键。 因此,通常更为有用的做法是选择分组联接的结果并放入一个也具有该键名的新类型中,如上一个示例所示。
var innerGroupJoinQuery2 = from category in categories join prod in products on category.ID equals prod.CategoryID into prodGroup from prod2 in prodGroup where prod2.UnitPrice > 2.50M select prod2;
左外部联接:在左外部联接中,将返回左侧源序列中的所有元素,即使它们在右侧序列中没有匹配的元素也是如此。 若要在 LINQ 中执行左外部联接,请将 DefaultIfEmpty 方法与分组联接结合起来,以指定要在某个左侧元素不具有匹配元素时产生的默认右侧元素。 null as the default value for any reference type, or you can specify a user-defined default type.‘ data-guid="81934d4ead5d5ef696a1d2b2f1959a52">可以使用 null 作为任何引用类型的默认值,也可以指定用户定义的默认类型。 下面的示例演示了用户定义的默认类型:
var leftOuterJoinQuery = from category in categories join prod in products on category.ID equals prod.CategoryID into prodGroup from item in prodGroup.DefaultIfEmpty(new Product { Name = String.Empty, CategoryID = 0 }) select new { CatName = category.Name, ProdName = item.Name };
select clause specifies the type of values that will be produced when the query is executed.‘ data-guid="b6de3901712949337d441d0d8e29fe95">select :
select clause specifies the type of values that will be produced when the query is executed.‘ data-guid="b6de3901712949337d441d0d8e29fe95">在查询表达式中,select 子句可以指定将在执行查询时产生的值的类型。 select clause itself.‘ data-guid="4ce984bf893b5ff654c163ea9352ce6f">该子句的结果将基于前面所有子句的计算结果以及 select 子句本身中的所有表达式。 select clause or a group clause.‘ data-guid="18aa9b66176436e3d5fe462fd2b2be88">查询表达式必须以 select 子句或 group 子句结束。select clause determines the type of the query variable queryHighScores.‘ data-guid="0b3b239959a93901a867608f02e69f63">select 子句产生的序列的类型决定了查询变量 queryHighScores 的类型。 select clause just specifies the range variable.‘ data-guid="80b6709b32bcd2519191ca01b7718a1c">在最简单的情况下,select 子句仅指定范围变量。 这会使返回的序列包含与数据源具有相同类型的元素。 Type Relationships in LINQ Query Operations (C#).‘ data-guid="f31944f45ee3f79acdd5c72b8a8d46f7">select clause also provides a powerful mechanism for transforming (or projecting) source data into new types.‘ data-guid="5914c2d46f313c63c5cbd1c9c0206af3">select 子句还提供了一种功能强大的机制,可用于将源数据转换(或投影)为新类型。
Lambda 表达式
delegates or expression tree types.‘ data-guid="2fe74a4350c8bc41522d776330618868">Lambda 表达式是一个可用于创建委托或表达式树类型的匿名函数。 通过使用 lambda 表达式,可以写入可作为参数或返回为函数调用值的本地函数。 Lambda 表达式对于编写 LINQ 查询表达式特别有用。=>, and you put the expression or statement block on the other side.‘ data-guid="35d22bcb09811da5e0ec07f34c10c0a8">若要创建 Lambda 表达式,必须在 Lambda 运算符 => 左侧指定输入参数(如果有),然后在另一侧输入表达式或语句块。 x => x * x specifies a parameter that’s named x and returns the value of x squared.‘ data-guid="c072edd5f07566e3185446a6649f1fe0">例如,lambda 表达式 x => x * x 指定名为 x 的参数并返回 x 的平方。 您可以按照以下示例将此表达式分配给委托类型:
delegate int del(int i); static void Main(string[] args) { del myDelegate = x => x * x; int j = myDelegate(5); //j = 25 }
=> 运算符具有与赋值运算符 (=) 相同的优先级,并且是右结合运算符。
expression lambda.‘ data-guid="f5da5eca09d4c30e6b9d98f5620d3e32">表达式在右边的 Lambda 表达式称为“Lambda 表达式”。 Lambda 表达式返回表达式的结果,并采用以下基本形式:
(input parameters) => expression
只有在 Lambda 有一个输入参数时,括号才是可选的;否则括号是必需的。 两个或更多输入参数由括在括号中的逗号分隔:
(x, y) => x == y
有时,编译器难于或无法推断输入类型。 如果出现这种情况,您可以按以下示例中所示方式显式指定类型:
(int x, string s) => s.Length > x
LINQ 中的查询语法和方法语法
在表示语言集成查询 (LINQ) 使用 LINQ 性查询语法,文档中的多数查询编写。 但是,在中,当编译代码时,必须将查询语法转换方法需要 .NET 公共语言运行时 (CLR)。 这些方法调用标准查询运算符,的名称类似 Where、Select、GroupBy、Join、Max和 Average。 可以调用这些方法直接使用方法语法而不是查询语法。查询语法和方法语法语义相同的,但是,许多人员发现查询语法更简单、更易于阅读。 某些查询必须表示为方法调用。 例如,必须使用方法调用表示检索元素的数量与指定的条件的查询。 还必须使用方法需要检索元素的最大值在源序列的查询。 System.Linq namespace generally uses method syntax.‘ data-guid="1ed75d85f92433b1199a541b5b5fe9c3">System.Linq 命名空间中的标准查询运算符的参考文档通常使用方法语法。 因此,即使在开始编写 LINQ 查询时,熟悉如何在查询和查询表达式本身中使用方法语法也非常有用。
standard query operators are the methods that form the Language-Integrated Query (LINQ) pattern.‘ data-guid="6116bc1457f47d99de18ac78c9b44ccb">“标准查询运算符”是组成语言集成查询 (LINQ) 模式的方法。 IEnumerable<T> interface or the IQueryable<T> interface.‘ data-guid="c0b59a96fa2f886cb4c74f14f97ab996">大多数这些方法都在序列上运行,其中的序列是一个对象,其类型实现了 IEnumerable<T> 接口或 IQueryable<T> 接口。 标准查询运算符提供了包括筛选、投影、聚合、排序等功能在内的查询功能。
IEnumerable<T> and the other that operates on objects of type IQueryable<T>.‘ data-guid="eab6a6314aeecfd618f02d60f4900120">共有两组 LINQ 标准查询运算符,一组在类型为 IEnumerable<T> 的对象上运行,另一组在类型为 IQueryable<T> 的对象上运行。 Enumerable and Queryable classes, respectively.‘ data-guid="ec1dcddd9b010f808060824a9114f216">构成每组运算符的方法分别是 Enumerable 和 Queryable类的静态成员。 extension methods of the type that they operate on.‘ data-guid="8c906313d39afa116babb25b8b80ffa2">这些方法被定义为作为方法运行目标的类型的“扩展方法”。 这意味着可以使用静态方法语法或实例方法语法来调用它们。
IEnumerable<T> or IQueryable<T>.‘ data-guid="e001e287c88bcb58f2b6c589fc55a6e6">此外,许多标准查询运算符方法运行所针对的类型不是基于 IEnumerable<T> 或 IQueryable<T> 的类型。 Enumerable type defines two such methods that both operate on objects of type IEnumerable.‘ data-guid="6275fac4fb94626581bd1792375126e4">Enumerable 类型定义两个此类方法,这些方法都在类型为 IEnumerable 的对象上运行。 Cast<TResult>(IEnumerable) and OfType<TResult>(IEnumerable), let you enable a non-parameterized, or non-generic, collection to be queried in the LINQ pattern.‘ data-guid="7007dc47c61ea0c01bc9102331d224e7">利用这些方法(Cast<TResult>(IEnumerable) 和 OfType<TResult>(IEnumerable)),您将能够在 LINQ 模式中查询非参数化或非泛型集合。 这些方法通过创建一个强类型的对象集合来实现这一点。 Queryable class defines two similar methods, Cast<TResult>(IQueryable) and OfType<TResult>(IQueryable), that operate on objects of type Queryable.‘ data-guid="a4247f9753b25d062ed27e4fc2babab5">Queryable 类定义两个类似的方法(Cast<TResult>(IQueryable) 和 OfType<TResult>(IQueryable)),这些方法在类型为 Queryable 的对象上运行。
各个标准查询运算符在执行时间上有所不同,具体情况取决于它们是返回单一值还是值序列。 Average and Sum) execute immediately.‘ data-guid="9615956c4f21e267f73c81e6705f1f94">返回单一值的方法(例如 Average 和 Sum)会立即执行。 返回序列的方法会延迟查询执行,并返回一个可枚举的对象。
IEnumerable<T>, the returned enumerable object captures the arguments that were passed to the method.‘ data-guid="79c9a8c4511632be04f034f58c418f3e">对于在内存中集合上运行的方法(即扩展 IEnumerable<T> 的那些方法),返回的可枚举对象将捕获传递到方法的参数。 在枚举该对象时,将使用查询运算符的逻辑,并返回查询结果。
IQueryable<T> do not implement any querying behavior, but build an expression tree that represents the query to be performed.‘ data-guid="ebd977b4b777cdb980cd965741b06106">与之相反,扩展 IQueryable<T> 的方法不会实现任何查询行为,但会生成一个表示要执行的查询的表达式树。 IQueryable<T> object.‘ data-guid="2d2f8f22e5cccb41cdacf86420e76f87">查询处理由源 IQueryable<T> 对象处理。
可以在一个查询中将对查询方法的调用链接在一起,这就使得查询的复杂性可能会变得不确定。