linq基础学习-20110424
目 录
1、百度百科:
(1) 基本概念
值 |
描述 |
LINQ,语言集成查询(Language INtegrated Query)是一组用于c#和Visual Basic语言的扩展。它允许编写C#或者Visual Basic代码以查询数据库相同的方式操作内存数据。 | |
|
LINQ 提供了一条更常规的途径即给 .Net Framework 添加一些可以应用于所有信息源( all sources of information )的具有多种用途( general-purpose )的语法查询特性( query facilities ),这是比向开发语言和运行时( runtime )添加一些关系数据( relational )特性或者类似 XML 特性( XML-specific )更好的方式。这些语法特性就叫做 .NET Language Integrated Query (LINQ) 。 |
|
|
(2) 基础知识
值 |
描述 |
命名空间:System.Linq; 选择3.5以上版本的.NET Framework之后,创建的新项目中会自动包含System.Linq的命名空间。 | |
关键词 |
from, select, in, where, group by, order by... |
语义 |
from 临时变量 in 集合对象或数据库对象 where 条件表达式 [order by条件] select 临时变量中被查询的值 [group by 条件] LINQ的查询返回值的类型是临时变量的类型,可能是一个对象也可能是一个集合。并且LINQ的查询表达式是在最近一次创建对象时才被编译的。LINQ的查询一般跟var关键字一起联用 (什么是var?匿名对象) 。 |
|
|
(3) 语法实例
值 |
描述 |
|
|
|
其中的 into 关键字表示 将前一个查询的结果视为后续查询的生成器。 |
|
|
(4) Linq的内部执行原理浅析- PLINQ
值 |
描述 |
优点 |
1、无需复杂学习过程即可上手 2、编写更少代码即可创建完整应用。 3、更快开发错误更少的应用程序。 4、无需求助奇怪的编程技巧就可合并数据源。 5、让新开发者开发效率更高。 |
PLINQ |
PLINQ原名为Parallel LINQ,支持XML和内存中的数据集合。执行于远程服务器上的查询语句(例如LINQ to SQL)显然无法实现这个功能。 |
LINQà PLINQ |
只需要在查询语句中From子句所指定的数据源的最后添加.AsParallel()即可。 |
PLINQ 三种执行方式 |
第一种是管道处理:一个线程用来读取数据源,而其他的线程则用来处理查询语句,二者同步进行——虽然这个单一的消费线程可能并不那么容易与多个生产线程同步。不过若是能够仔细配置好负载平衡的话,仍然会极大地减少内存占用。 第二种模式叫做“stop and go”,用于处理结果集需要被一次返回时(例如调用ToList、ToArray或对结果排序)的情况。在这种模式下,将依次完成各个处理过程,并将结果统一返回给消费线程。这个模式在性能上将优于第一种模式,因为它省去了用来保持线程同步所花费的开销。 最后一种方法叫做“inverted enumeration”。该方法并不需要实现收集到所有的输出,然后在单一的线程中处理,而是将最终调用的函数通过ForAll扩展传递到每个线程中。这是目前为止最快的一种处理模式,不过这需要传递到ForAll中的函数是线程安全的,且最好不包含任何lock之类的互斥语句。 |
注意 |
若是PLINQ中任意的一个线程抛出异常,那么所有的其他线程将会被终止。若是抛出了多个异常,那么这些异常将被组合成一个MultipleFailuresException类型的异常,但每个异常的调用堆栈仍会被保留。 |
|
|
(5) 相关问题:为何以from开头,而非select?
值 |
描述 |
描述 |
为何 LINQ 查询语法是以 from 关键字开头的,而不是以 select 关键字开头的?select 开头这种写法跟SQL的写法更接近,更易懂呀? |
简答一 |
简单来说,为了IDE的智能感知(Intelisence)这个功能,select 关键字放在后面了。 |
详细 |
假设你要书写这样的代码:Select p.Name, p.Age From p In persons Where xxx ,代码是一个个字符输入的。 我们在写到 p in persons 之前,p 的类型是无法推测的,所以写 Select p. 的时候,Name之类的属性不会弹出智能提示来。 这样就需要先去写 From 这句,再回来写 Select。 |
|
2、Linq大观园网站:
(1) Linq基础
值 |
描述 | |
| ||
三个操作 |
| |
foreach |
在 foreach 语句中执行查询,而 foreach 要求使用 IEnumerable 或 IEnumerable<(Of <(T>)>)。 支持 IEnumerable<(Of <(T>)>) 或派生接口(如泛型 IQueryable<(Of <(T>)>))的类型称为“可查询类型”。 | |
执行 |
查询变量本身只是存储查询命令。实际的查询执行会延迟到在 foreach 语句中循环访问查询变量时发生。叫“延迟执行”! | |
|
| |
(2) 延迟执行与立即执行
值 |
描述 |
Linq中是执行都是延迟执行,即在foreach中执行,立即执行需要强制! | |
延迟执行 |
查询变量本身只是存储查询命令。实际的查询执行会延迟到在 foreach 语句中循环访问查询变量时发生。 |
强制立即执行 |
强制立即执行任意查询并缓存其结果,可以调用 ToList<(Of <(TSource>)>) 或 ToArray<(Of <(TSource>)>) 方法。 |
|
|
(3) 基本查询操作:数据源-筛选-排序-分组-联接-选择
值 |
描述 |
如本节标题所述,linq查询主要包括:获取数据源、筛选、排序、分组、联接、选择。 | |
获取 数据源 |
在 LINQ 查询中,第一步是指定数据源。使用 from 子句的目的是引入数据源 (customers) 和范围变量 (cust)。 queryAllCustomers = from cust in customers select cust; 范围变量类似于 foreach 循环中的迭代变量,但在查询表达式中,实际上不发生迭代。执行查询时,范围变量将用作对 customers 中的每个后续元素的引用。因为编译器可以推断 cust 的类型,所以您不必显式指定此类型。 |
筛选 |
使用 where 子句生成结果。筛选器指定从源序列中排除哪些元素。 where cust.City==" where cust.City == " |
排序 |
orderby 子句将使返回的序列中的元素按照被排序的类型的默认比较器进行排序。 orderby cust.Name ascending 若要按相反顺序(从 Z 到 A)对结果进行排序,请使用 orderby…descending 子句。 |
分组 |
使用 group 子句,您可以按指定的键分组结果。 from cust in customers group cust by cust.City;//按照city给cust分组 如果您必须引用组操作的结果,可以使用into关键字来创建可进一步查询的标识符。 from cust in customers group cust by cust.City into custGroup where custGroup.Count() > 2 orderby custGroup.Key select custGroup; //该查询只返回那些包含两个以上的客户的组。 |
联接 |
联接join运算创建数据源中没有显式建模的序列之间的关联。 例如,您可以执行联接来查找符合以下条件的所有客户:位于巴黎,且从位于伦敦的供应商处订购产品。 |
选择 (投影) |
select 子句生成查询结果并指定每个返回的元素的“形状”或类型。当 select 子句生成除源元素副本以外的内容时,该操作称为“投影”。使用投影转换数据是 LINQ 查询表达式的一种强大功能。 |
|
|
(4) LINQ 和泛型类型
值 |
描述 |
| |
两个基本概念 |
•当您创建泛型集合类(如 List<(Of <(T>)>))的实例时,您将“T”替换为列表将包含的对象的类型。例如,字符串列表表示为 List,Customer 对象列表表示为 List。泛型列表是强类型的,且提供了比将其元素存储为 Object 的集合更多的好处。如果您尝试将 Customer 添加到 List,则会在编译时出现一条错误。泛型集合易于使用的原因是您不必执行运行时类型强制转换。 •IEnumerable<(Of <(T>)>) 是一个接口,通过该接口,可以使用 foreach 语句来枚举泛型集合类。泛型集合类支持 IEnumerable<(Of <(T>)>),就像非泛型集合类(如 ArrayList)支持 IEnumerable。 |
|
LINQ 查询中的 IEnumerable 变量 LINQ 查询变量类型化为 IEnumerable<(Of <(T>)>) 或派生类型,如 IQueryable<(Of <(T>)>)。当您看到类型化为 IEnumerable 的查询变量时,这只意味着在执行该查询时,该查询将生成包含零个或多个 Customer 对象的序列。 IEnumerable<Customer> customerQuery = from cust in customers where cust.City == " select cust; foreach (Customer customer in customerQuery) { Console.WriteLine(customer.LastName + ", " + customer.FirstName); } |
|
var 关键字来避免使用泛型语法。var 关键字指示编译器通过查看在 from 子句中指定的数据源来推断查询变量的类型;以下语句与上面是等价的: var customerQuery2 = from cust in customers where cust.City == " select cust; |
|
|
(5) 查询操作中的类型关系
值 |
描述 |
LINQ 查询操作在数据源、查询本身及查询执行中是强类型的。查询中变量的类型必须与数据源中元素的类型和 foreach 语句中迭代变量的类型兼容。此强类型保证在编译时捕获类型错误,以便可以在用户遇到这些错误之前更正它们。 | |
不转换 源数据 |
1、数据源的类型参数决定范围变量的类型。 2、选择的对象的类型决定查询变量的类型。此处的 name 为一个字符串。因此,查询变量是一个 IEnumerable。 3、在 foreach 语句中循环访问查询变量。因为查询变量是一个字符串序列,所以迭代变量也是一个字符串。 |
转换 源数据 |
1、数据源的类型参数决定范围变量的类型。 2、select 语句返回 Name 属性,而非完整的 Customer 对象。因为 Name 是一个字符串,所以 custNameQuery 的类型参数是 string,而非 Customer。 3、因为 custNameQuery 是一个字符串序列,所以 foreach 循环的迭代变量也必须是 string。 |
稍微复杂 的转换 |
1、数据源的类型参数始终为查询中的范围变量的类型。 2、因为 select 语句生成匿名类型,所以必须使用 var 隐式类型化查询变量。 3、因为查询变量的类型是隐式的,所以 foreach 循环中的迭代变量也必须是隐式的。 |
编译器 推断类型 |
虽然您应该了解查询操作中的类型关系,但是您也可以选择让编译器为您执行全部工作。关键字 var 可用于查询操作中的任何局部变量。 |
|
|
|
|
(6) LINQ To DataSet
值 |
描述 |
LINQ to DataSet主要是提供对离线数据的支持,只有在填充DataSet之后,我们才能使用LINQ to DataSet来查询数据。其功能主要是通过System.Data.DataRowExtions和System.Data.DataTableExtensions两个静态类中的扩展方法来公开的。LINQ to DataSet是LINQ to ADO.Net中的一部分,但这部分所占比重非常小,内容也比较少。 | |
|
(from s in newTable.AsEnumerable() where s.Field<string>("Name") == "PiPi Zhu" select s).Single<DataRow>().SetField("Name", "George Oscar Bluth"); |
总结 |
在使用LINQ to DataSet的时候需要注意以下几个方面: 1、 在对IEnumeralbe进行数据行的集合操作如Distinct, Except, Union, Intersect, SequenceEqual时,需要使用System.Data.DatarowComparer.Default作为比较器作为输入参数,以保证对DataRow是进行值比较,而不是引用比较。当然,如果GroupBy或者其他操作的key的类型是DataRow时,也需要使用此比较器,以使我们得到我们期望的行为。 2 、SetField可以将字段值设置为null,并且SetField方法将自动将其转换为DBNull.Value. 3 、Field可以完成从DBNull.Value到null的转换。也就是说,如果该字段的值是DBNull.Value 时,Field方法将自动将其转为null并返回。这个方法是强类型的,不需要象通过列名或者列索引返回字段值一样将Object类型进行造型成需要的类型(值类型进行拆箱操作),(如果字段的值是DBNull.Value时进行造型还将导致抛出异常)Field扩展方法自动做了这些处理,省去了开发人员手动进行这些处理的麻烦。 4 、缺省情况下,数据行的Original版本中是没有值的,试图访问时将导致异常发生。当然,可以在访问之前使用DataRow.HasVersion来进行判断,以防止异常的发生。也可以通过调用DataRow.AcceptChanges方法来建立Original版本来避免异常的发生。不带LoadOptions参数的CopyToDataTable扩展方法也会为返回的DataTable自动建立DataRow的Original和Current版本. 5 当使用带LoadOptions输入参数的CopyToDataTable扩展方法时,必须为目标DataTable指定主键列,否则,该函数只是将源DataTable追加到目标DataTable的最后面。可能达不到期望更新的结果。 |