LINQ概述#

  1. 语言集成查询(Language intergrated Query,LINQ)在C#编程语言中集成了查询语法。
  2. 可以使用相同的语法访问不同的数据源
  3. 提供了不同数据源的抽象层,所有可以使用相同的语法

LINQ查询

var query= from r in [list] where r.x=='xxx' orderby r.x desceding select r;

查询表达式必须以from子句开头,以select或者group 结束 变量query只是指定了查询,查询语句不是通过赋值来执行,只要是使用了foreach循环查询,才正式执行

扩展方法

编译器会转换LINQ查询,以调用方法而不是LINQ查询。LINQ为IEnumberable接口提供了各种扩展方法,已便用户在实现该接口的任意集合上使用LINQ查询。

扩展方法可以吧方法添加到实现某个特性接口的任何类中,这样多个类就可以使用相同的实现代码。

public static class StringExtension
{
public static void Foo(this string s)
{
System.Console.WriteLine("Foo invoked for {0}",s);
}
} string s="Hello";
s.Foo();

扩展方法不能访问它的扩展类型的私有成员。调用扩展方法只是调用静态方法的一种新语法

string s="Hello";
StringExtension.Foo(s);

只需要导入包含该类的命名空间就使用扩展方法

定义LINQ扩展方法的一个是在System.Linq命名空间Enumerable.

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);

yield 关键字向编译器指示它所在的方法是迭代器块。编译器生成一个类来实现迭代器块中表示的行为。在迭代器块中,yield 关键字与 return 关键字结合使用,向枚举器对象提供值。这是一个返回值,例如,在 foreach 语句的每一次循环中返回的值。yield 关键字也可与 break 结合使用,表示迭代结束。

例子:

yield return ;

yield break;

在 yield return 语句中,将计算 expression 并将结果以值的形式返回给枚举器对象;expression 必须可以隐式转换为 yield 类型的迭代器。

在 yield break 语句中,控制权将无条件地返回给迭代器的调用方,该调用方为枚举器对象的 IEnumerator.MoveNext 方法(或其对应的泛型 System.Collections.Generic.IEnumerable)或 Dispose 方法。

方法返回IEnumerable,所有可以使用前面的结果依次调用这些方法。

Func是系统定义的泛型委托 public delegate TResult Func<in T, out TResult>(T arg);

推迟查询执行

在运行期间定义的查询表达式时,查询不会立刻运行。查询会在迭代数据项是运行。

       var names = new List<string> {"Nino","Windy","Juan" };
//延时查询的案例
var namesWithJ = from n in names
where n.StartsWith("J")
orderby n
select n;
Console.WriteLine("First iteration");
//每次迭代才会执行查询
foreach( var name in namesWithJ)
{
Console.WriteLine(name);
}
Console.WriteLine();
names.Add("John");
names.Add("Jim");
Console.WriteLine("Second iteration");
//每次迭代才会执行查询
foreach (var name in namesWithJ)
{
Console.WriteLine(name);
}
Console.ReadLine();

当然,每次在迭代是才使用查询,调用扩展方法,在大多数使用很有效,但是有些情况下我们想立刻执行,可以调用扩展方法ToArray()、ToList()

两次迭代之前输出保持不变,但集合中的值已经改变了。

标准的查询操作符

在Enumerable类定义很多标准查询操作符

筛选

使用where 子句,可以合并多个表达式,传递给where子句的表示结果类型应该是布尔类型:

var namesWithJ = from n in names
//组合表达式,返回布尔类型即可
where n.StartsWith("J") && n.Length > 3
orderby n
select n;

映射到扩展方法Where()和Select(),以下代码会与前面的LINQ查询非常类似的结果:

var namesWithJV2 = names.
Where(n => n.StartsWith("J") && n.Length > 3).
Select(n => n);

用索引筛选

不能使用LINQ查询是Where方法的重载,可以传递int参数--索引。可以在表达式中使用这个索引,执行基于索引的计算。

 //
// 摘要:
// 基于谓词筛选值序列。将在谓词函数的逻辑中使用每个元素的索引。
//
// 参数:
// source:
// 要筛选的 System.Collections.Generic.IEnumerable<T>。
//
// predicate:
// 用于测试每个源元素是否满足条件的函数;该函数的第二个参数表示源元素的索引。
//
// 类型参数:
// TSource:
// source 中的元素的类型。
//
// 返回结果:
// 一个 System.Collections.Generic.IEnumerable<T>,包含输入序列中满足条件的元素。
//
// 异常:
// System.ArgumentNullException:
// source 或 predicate 为 null。
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, int, bool> predicate);

案例(LINQ无法实现,Where方法可以实现)

  var namesWithJV2 = names.
Where((n, index) => n.StartsWith("J") && n.Length > 3 && index % 2 != 0).
Select(n => n);

类型筛选

为了进行基于类型的筛选,可以使用OfType()扩展方法。

 object[] data = { "one", 2, 3, "four", "five", 6 };
//筛选出类型是string的值
var query = data.OfType<string>();
foreach (var s in query)
{
Console.WriteLine(s);
}

复合的from子句

如果需要根据对象的一个成员进行筛选,而且该成员本身是一个系列,就可以使用复合from子句。

 //复合from语句
var namesWithJ = from n in names
from c in n.ToCharArray()
where c.Equals('N')
orderby n
select n;

C#编译器吧复合的from子句和LINQ查询转换为SelectMany()扩展方法,该方法用于迭代序列的序列。

    //
// 摘要:
// 将序列的每个元素投影到 System.Collections.Generic.IEnumerable<T>,并将结果序列合并为一个序列,并对其中每个元素调用结果选择器函数。
//
// 参数:
// source:
// 一个要投影的值序列。
//
// collectionSelector:
// 一个应用于输入序列的每个元素的转换函数。
//
// resultSelector:
// 一个应用于中间序列的每个元素的转换函数。
//
// 类型参数:
// TSource:
// source 中的元素的类型。
//
// TCollection:
// collectionSelector 收集的中间元素的类型。
//
// TResult:
// 结果序列的元素的类型。
//
// 返回结果:
// 一个 System.Collections.Generic.IEnumerable<T>,其元素是对 source 的每个元素调用一对多转换函数
// collectionSelector,然后将那些序列元素中的每一个及其相应的源元素映射为结果元素的结果。
//
// 异常:
// System.ArgumentNullException:
// source 或 collectionSelector 或 resultSelector 为 null。
public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector);

第一个参数是隐试参数,第二是collectionSelector委托,其中定义内部序列,第三是resultSelector委托,对于第二个序列调用该委托。

暂时没有理解SelectMany方法,后续再研究

排序

要对序列排序,使用orderby子句。

orderby子句解析为OrderBy()方法。

orderby descending对应OrderByDescending()方法。

两个方法返回的都是IOrderedEnumerable,该接口派生 IEnumerable



增加CreateOrderedEnumerable方法。用于进一步给序列排序。可以使用ThenBy()继续排序。

在linq查询是,只需要把所有用户排序的关键字(用逗号分隔开)添加到orderby子句中。

分组

要根据一个关键字值对查询结果分组,可以使用group子句。

内连接

使用join子句可以根据特点的条件合并两个数据源,但之前要获得两个要连接的列表。

左外连接

左外连接返回左边序列中全部元素,即使它在右边的序列中没有匹配的元素。

左外连接使用join子句和DefaultifEmpty方法定义。

分区

扩展方法Take()和Skip()等分区操作可用于分页。

Skip():先忽略根据的页面大小和实际页面计算的项数,

Take():提取一点数量的项

聚合操作符

聚合操作符(Count,Sum,Min,Max,Average,Aggergate)不返回一个序列,而返回一个值。

因为同一个查询需要使用相同的计算超过一次,所以是let子句定义一个变量。

Aggregate()方法,可以传递一个lambda表达式,该表达式对所有值进行聚合。

聚合函数会影响linq的延时加载特性。

转换操作符

在前面提到,查询可以推迟到访问数据项时再执行。再迭代中使用查询时,查询才会执行。而使用转换操作符会立即执行查询,把查询结果放在数组、列表或者字典中。

把返回的对象放在列表中并没有那么简单。对于复杂的可以使用新类Lookup<>来操作。

如需要对非类型泛化的集合上使用LINQ查询们可以使用Case方法。

生成操作符

生成操作符Range()、Empty()、Repear()不是扩展方法,而是返回序列的正常静态方法。

Enumerable.Range(1,20):1 2 3 .....20;

Empty 返回一个不返回值的迭代器;

Repeat方法返回一个迭代器;

表达式树

在LINQ TO objects中,扩展方法需要将一个委托类型作为参数,这样就可以将lambda表达式赋予参数。

lambda表达式也可以赋予Expression类型的参数。C#编译器根据类型给lambda表达式定义的不同行为。如果类型是Expression,编译器就从lambda表达式中创建一个表达式树,并存储在程序集中。这样就可以在运行期间分享表达式树,并进行优化,以便于查询数据源。

在Enumberable类不是唯一定义扩展方法Where()的类。在Queryable类也定义了Where()扩展方法。两者定义是不一样的。

    // 摘要:
// 基于谓词筛选值序列。
//
// 参数:
// source:
// 要筛选的 System.Linq.IQueryable<T>。
//
// predicate:
// 用于测试每个元素是否满足条件的函数。
//
// 类型参数:
// TSource:
// source 中的元素的类型。
//
// 返回结果:
// 一个 System.Linq.IQueryable<T>,包含满足由 predicate 指定的条件的输入序列中的元素。
//
// 异常:
// System.ArgumentNullException:
// source 或 predicate 为 null。
public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate); //Enumerable // 摘要:
// 通过合并元素的索引将序列的每个元素投影到新表中。
//
// 参数:
// source:
// 一个值序列,要对该序列调用转换函数。
//
// selector:
// 一个应用于每个源元素的转换函数;函数的第二个参数表示源元素的索引。
//
// 类型参数:
// TSource:
// source 中的元素的类型。
//
// TResult:
// selector 返回的值的类型。
//
// 返回结果:
// 一个 System.Collections.Generic.IEnumerable<T>,其元素为对 source 的每个元素调用转换函数的结果。
//
// 异常:
// System.ArgumentNullException:
// source 或 selector 为 null。
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, TResult> selector);

Queryable定义的委托类型是Expression。

除了使用委托之外,编译器还会把表达式树放在程序集中。表达式树可以在运行期间读取。

表达式树派生自抽象基类Expression的类中构建。

创建一个表达式树,并将该树存储在程序集中。之后在运行期间使用这个树,创建一个应用于底层数据源的优化查询。

使用Expression类型的一个是ADO.NET Entity Framework 和WCF的数据服务的客户端提供程序。这样访问数据库的LINQ提供程序就可以读取表达式,创建一个运行期间优化的查询,从数据库中获取数据。

无论是用Func还是Expression参数传递,lambda表达式都是相同的。只是编译器的行为不同,它根据source参数来选择。在EF中返回的实现了IQueryable接口的ObjectQuery对象,因此EF中是Queryable类的扩展方法。

参考图书《C#高级编程》

推进阅读 http://www.cnblogs.com/jesse2013/p/happylambda.html

LINQ笔记的更多相关文章

  1. 简单的Linq笔记

    最近带一个新人,被问到Linq的一点东西,回答他后,自己记录下,防止自己懵逼. Linq中查询一个表中指定的几个字段: var ts = t.FindAllItems().Where(P => ...

  2. 简单Linq笔记

    Linq是.net 3.5才引入的 要引入命名空间System.Linq. Linq  to XML要引入System.Xml.Linq Linq to ADO.NET要引入System.Data.L ...

  3. C#高级编程(第9版) 第11章 LINQ 笔记

    概述语言集成查询(Language Integrated Query, LINQ)在C#编程语言中集成了查询语法,可以用相同的语法访问不同的数据源.LINQ提供了不同数据源的抽象层,所以可以使用相同的 ...

  4. 《C#本质论》读书笔记(15)使用查询表达式的LINQ

    15.1 查询表达式的概念 简单的查询表达式 private static void ShowContextualKeywords1() { IEnumerable<string> sel ...

  5. linq学习笔记

    最近在学习linq的一些基础知识,看了c#高级编程及阅读了园子内部几篇优秀的博文,有所体会,感觉应该记录下来,作为以后复习使用.都是一些最基础的知识,大致分为三个部分:linq预备知识:linq查询: ...

  6. Dynamic CRM 2013学习笔记(三十)Linq使用报错 A proxy type with the name account has been defined by another assembly

    在CRM中使用linq时,有时会报这个错误: A proxy type with the name account has been defined by another assembly. Curr ...

  7. Silverlight项目笔记1:UI控件与布局、MVVM、数据绑定、await/async、Linq查询、WCF RIA Services、序列化、委托与事件

    最近从技术支持转到开发岗,做Silverlight部分的开发,用的Prism+MVVM,框架由同事搭好,目前做的主要是功能实现,用到了一些东西,侧重于如何使用,总结如下 1.UI控件与布局 常用的主要 ...

  8. Linq to XML 读取XML 备忘笔记

    本文转载:http://www.cnblogs.com/infozero/archive/2010/07/13/1776383.html Linq to XML 读取XML 备忘笔记 最近一个项目中有 ...

  9. asp.net、mvc、ajax、js、jquery、sql、EF、linq、netadvantage第三方控件知识点笔记

    很简单,如下: 父页面:(弹出提示框) function newwindow(obj) { var rtn = window.showModalDialog('NewPage.htm','','sta ...

随机推荐

  1. HDU5879(打表)

    Cure Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  2. JS与浏览器的几个兼容性问题

    第一个:有的浏览器不支持getElementsByClassName(),所以需要写一个function()来得到需要标签的class,然后进行class的增加.删除等操作. 第二个:在需要得到特定标 ...

  3. Runloop -------iOS

    正文 1.Runloop是什么? Runloop是线程的基础架构部分.是一个事件循环处理.是用来不停的调配工作(可以节省CPU)和处理输入事件(输入源(input source)和定时源(timer ...

  4. Java String类练习题

    题目:1. 给定一个字符串,判断该字符串中是否包含某个子串.如果包含,求出子串的所有出现位置.如:"abcbcbabcb34bcbd"中,"bcb"子串的出现位 ...

  5. java读取Excel文档插入mysql

    /** * 读取excel插入myslq */package com.excel; import java.io.BufferedInputStream;import java.io.File;imp ...

  6. Python之文件与目录操作及压缩模块(os、shutil、zipfile、tarfile)

    Python中可以用于对文件和目录进行操作的内置模块包括: 模块/函数名称 功能描述 open()函数 文件读取或写入 os.path模块 文件路径操作 os模块 文件和目录简单操作 zipfile模 ...

  7. mysql远程连接权限

    环境:mysql6.0 .Navicat Premium 用户名:root 密码:123456  本地连接无问题 远程连接mysql的时候应该碰到Navicat Premium 报错. 错误代码是11 ...

  8. JAVA设计模式:模板设计模式

    1.模板设计模式,是为了体现继承的作用.它主要的作用就是在类中定义一些公共的方法和标准,而其具体的实现则叫给其子类来根据子类具体的行为来实现:因为模板设计模式中必经还有一些自己的方法不是抽象的方法,只 ...

  9. 连连看的原生JS实现

    那天闲来无事,便想找个小游戏来打发时间,后来便找到了连连看, 玩了一会儿感觉无聊,想到各位高手用JS做的各种小游戏,便想自己也来做一个,于是便有了这几天的成果. 代码是用 原生JS 实现的,只是用来学 ...

  10. SQL Server 2008R2 企业版 百度云下载地址

    SQL Server 2008R2 (百度云下载地址:链接: http://pan.baidu.com/s/1mi34II8 密码: yc1w)   ASP.NET MVC4.0+ WebAPI+Ea ...