第二十一章、使用查询表达式来查询内存中的数据

  什么是语言集成查询(LINQ)

  对从应用程序代码中查询数据的机制进行了“抽象”。这个功能称为“语言集成查询”(Language Integrated Query)。

  LINQ的设计者大量借鉴了关系数据库管理系统(例如Microsoft SQL Server)的处理方式,将“数据库查询语句”与“数据在数据库中的内部格式”分隔开。LINQ的语法和语义和SQL很像,具有许多相同的优势。要查询的数据的内部结构发生改变后,不必修改查询代码。注意,虽然LINQ和SQL看起来很像,但LINQ更加灵活,而且能处理范围更大的逻辑数据结构。

  LINQ要求数据用实现了IEnumerable或IEnumerable接口的数据结构进行存储。具体使用什么数据结构不重要。可以是数组、HashSet、Queue或者其他任何集合类型(甚至可自己定义)。唯一的要求就是这种类型是“可枚举”的。

  客户信息

  地址信息 

  假定客户和地址信息存储在如下的customers和addresses数组中。

  var customers = new[] {

  new {CustomerID = 1,FirstName = "Kim",LastName = "Abercrombie",CompanyName = "Alpine Ski House"},

  new {CustomerID = 2,FirstName = "Jeff",LastName = "Hay",CompanyName = "Coho Winery"},

  new {CustomerID = 3,FirstName = "Charlie",LastName = "Herb",CompanyName = "Alpine Ski House"},

  new {CustomerID = 4,FirstName = "Chris",LastName = "Preston",CompanyName = "Trey Research"},

  new {CustomerID = 5,FirstName = "Dave",LastName = "Barnett",CompanyName = "Wingtip Toys"},

  new {CustomerID = 6,FirstName = "Ann",LastName = "Beebe",CompanyName = "Coho Winery"},

  new {CustomerID = 7,FirstName = "John",LastName = "Kane",CompanyName = "Wingtip Toys"},

  new {CustomerID = 8,FirstName = "David",LastName = "Simpson",CompanyName = "Trey Research"},

  new {CustomerID = 9,FirstName = "Greg",LastName = "Chapman",CompanyName = "Wingtip Toys"},

  new {CustomerID = 10,FirstName = "Tim",LastName = "Litton",CompanyName = "Wide World Importers"},

  };

  var addresses = new[] {

  new {CompanyName = "Alpine Ski House", City = "Berne", Country = "Switzerland"},

  new {CompanyName = "Coho Winery", City = "San Francisco", Country = "United States"},

  new {CompanyName = "Trey Research", City = "New York", Country = "United States"},

  new {CompanyName = "Wingtip Toys", City = "Landon", Country = "United Kingdom"},

  new {CompanyName = "Wide World Importers", City = "Tetbury", Country = "United Kingdom"},

  } ;

  查询数据

  为了显示由customers数组中每个客户的名字(FirstName)组成的列表,可以写一下代码:

  IEnumerable customerFirstNames = customers.Select(cust => cust.FirstName);

  foreach(string name in customerFirstNames )

  {

  Console.WriteLine(name);

  }

  Select方法允许从数组获取特定信息。传给Select方法的参数实际上是另一个方法,该方法从customers数组中获取一行,并返回从那一行选择的数据。可用自定义的方法执行这个任务,但最简单的机制是用Lambda表达式定义匿名方法,就像上例展示的那样。目前要注意以下3个重点:

  1、cust变量是传给方法的参数。可认为cust是customers数组中的每一行的别名。

  2、Select方法目前还没开始获取数据;相反,它只是返回一个“可枚举”对象。稍后遍历它时,才会真正获取Select方法指定的数据。

  3、Select其实不是Array类型的方法。它是Enumerable类的扩展方法。Enumerable类位于System.Linq命名空间,它提供了大量静态方法来查询实现了泛型IEnumerable接口的对象。

  Select方法返回基于某具体类型的可枚举集合。如果希望枚举器返回多个数据项,例如返回每个客户的名字和姓氏,至少有以下两个方案:

  1、可以在Select方法中,将名字和姓氏连接成单独的字符串。实例如下:

  IEnumerable customerNames = customers.Select(cust => String.Format("{0}{1}",cust.FirstName,cust.LastName));

  2、可定义新类型来封装姓名和姓氏,并用Select方法构造这个类型的实例。例如:

  class FullName

  {

  public string FirstName{get;set;}

  public string LastName{get;set;}

  }

  .....

  IEnumerable customerName =  customers.Select(cust => new FullName

  {

  FirstName = cust. FirstName,

  LastName = cust.LastName

  });

  第二个选项本来应该是首选的。但如果FullName类型的作用仅限于此,就可考虑使用匿名类型,而不是专门为一个操作定义一个新类型。下面是匿名类型的例子:

  var customerName = customers.Select(cust => new{FirstName = cust. FirstName,LastName = cust.LastName});

  注意,这里使用var关键字定义可枚举的类型。集合中的对象类型是匿名的,所以不知道集合中的对象的具体类型。

  筛选数据

  Select方法允许“指定”(用更专业的术语来说,就是“投射”)想包含到可枚举集合中的字段。然而,有时希望对可枚举集合中包含的进行限制。例如,为了列出address数组中地址在美国的所有公司的名称,可以像下面这样使用Where方法。

  IEnumerable  usCompanies =

  addresses.Where(addr => String.Equals(addr.Country,"United States"))

  .Select(usComp => usComp.CompanyName);

  foreach(string name in usCompanies )

  {

  Console.WriteLine(name); //Coho Winery  Trey Research

  }

  首先应用Where方法,从而筛选出行;再应用Select方法,从而指定(或者说投射)其中特定的字段。

  排序、分组和聚合数据

  按特定顺序获取数据要使用OrderBy方法。与Select和Where方法相似,OrderBy也要求以一个方法作为实参。该方法标识了对数据进行排序的表达式

  IEnumerable  companyNames =

  addresses.OrderBy(addr => addr.CompanyName).Select(comp => comp.CompanyName);//升序

  foreach(string name in companyNames )

  {

  Console.WriteLine(name);

  }

  要求降序枚举数据,可以换用OrderByDescending方法。要按多个键排序,可以在OrderBy或OrderByDescending之后使用ThenBy或ThenByDescending。

  要按一个或多个字段中共同的值对数据进行分组,可以使用GroupBy方法。下例展示了如何按照国家对addresses数组中的公司进行分组。

  var companiesGroupedByCountry =

  addresses.GroupBy(addrs => addrs.Country);

  foreach(var companiesPerCountry in companiesGroupedByCountry )

  {

  Console.WriteLine("Country: {0}\t{1} companies", companiesPerCountry.Key, companiesPerCountry.Count())

  foreach(var companies in companiesPerCountry )

  {

  Console.WriteLine("\t{0}", companies.CompanyName);

  }

  }

  GroupBy方法不需要同Select方法将字段投射到结果。

  可直接为Select方法的结果使用许多汇总方法,例如Count,Max和Min等。例如:

  int numberOfCompanies = addresses.Select(addr => addr.CompanyName).Count();

  Console.WriteLine("Number of companies:{0}", numberOfCompanies );

  可用Distinct方法来删除重复

  int numberOfCountries = addresses.Select(addr => addr.Country).Distinct().Count();

  Console.WriteLine("Number of countries:{0}", numberOfCountries );

  联接数据

  和SQL一样,LINQ也允许根据一个或多个匹配键(common Key)字段来联接多个数据集。下例展示了如何显示每个客户的名字和姓氏,同时显示他们所在国家的名称:

  var companiesAndCustomers = customers.Select(c => new {c.FirstName,c.LastName,c.CompanyName})

  .Join(addresses, cust =>cust.CompanyName, addrs =>addrs.CompanyName,

  (custs,addrs) => new {custs.FirstName,custs.LastName,addrs.Country });

  foreach(var row in companiesAndCustomers )

  {

  Console.WriteLine(row);

  }

  使用查询操作符

  C#的设计者为语言添加了一系列查询操作符,允许开发人员使用与SQL更相似的语法来使用LINQ功能。

  var customerFirstNames  = from cust in customers

                                                    select cust.FirstName;

  编译时,C#编译器将上述表达式解析成对应的Select方法。from操作符为来源集合定义了别名,select操作符利用该别名指定了要获取的字段。

  var customerNames  = from c in customers

                                            select new {c.FirstName,c.LastName};

  where:

  var usCompanies = from a in addresses

                                      where String.Equals(a.Country,"United States")

                                      select a.CompanyName;

  orderby:

  var companyNames = from a in address

                                           orderby a.CompanyName

                                           select a.CompanyName;

  group by:

  var companiesGoupedByCountry = from a in addresses

                                                                  group a by a.Country;

  注意,和前面用GroupBy方法对数据进行分组的例子一样,这里不需要提供select操作符,而且可以和以前一样的代码遍历结果:

  foreach(var companiesPerCountry in companiesGroupedByCountry )

  {

  Console.WriteLine("Country: {0}\t{1} companies", companiesPerCountry.Key, companiesPerCountry.Count())

  foreach(var companies in companiesPerCountry )

  {

  Console.WriteLine("\t{0}", companies.CompanyName);

  }

  }

  可为返回的可枚举集合调用各种汇总函数,例如Count方法:

  int numberOfCompanies = (from a in addresses

                                                    select a.CompanyName).Count();

  int numberOfCountries = (from a in addresses

                                                  select a.Country).Distinct().Count();

  join:

  var citiesAndCustomers = from a in addresses

                                                  join c in customers

                                                  on a.CompanyName equals c.CompanyName

                                                  select new{c.FirstName,c.LastName,a.Country};

  LINQ和推迟求值

  使用LINQ定义可枚举集合时,不管是使用LINQ扩展方法,还是使用查询操作符,都应该记住这样一点:LINQ扩展方法执行时,应用程序不会真正构建集合;只有在遍历集合时,才会对集合进行枚举。也就是说,从执行一个LINQ查询之后,到取回这个查询所标识的数据之前,原始集合中的数据可能发生改变。但是,获取的始终是最新的数据。例如:

  var usCompanies = from a in addresses

                                     where String.Equals(a.Country,"United States")

                                     select a.CompanyName;

  除非使用以下代码遍历usCompanies 集合,否则addresses数据中数据不会获取,Where筛选器中指定的条件也不会求值:

  foreach(string name in usCompanies )

  {

  Console.WriteLine(name);

  }

  从定义usCompanies 集合到遍历这个集合,在此期间如果对addresses数组中的数据进行修改,就会看到新的数据。这个策略就是所谓的推迟求值。

  可在定义LINQ查询时强制求值,从而生成一个静态的、缓存的集合。这个集合是原始数据的拷贝。如果原始数据发生改变,这个拷贝中的数据是不会相应改变的。LINQ提供了ToList方法来构建静态List对象以包含数据的缓存拷贝。如下:

  var usCompanies = from a in addresses.ToList()

                                     where String.Equals(a.Country,"United States")

                                     select a.CompanyName;

分组查询:

var addresses = new[] {

new {EIRNo = 1, Charge = 20, CNTNo = "1"},

new {EIRNo = 2, Charge = 10, CNTNo = "2"},

new {EIRNo = 1, Charge = 5, CNTNo = "1"},

new {EIRNo = 2, Charge = 10, CNTNo = "2"},

};

var query = from c in addresses.AsEnumerable()

group c by new

{

c.EIRNo,

c.CNTNo

}

into s

select new

{

EIRNo = s.Key.EIRNo,

CNTNo = s.Key.CNTNo,

Charge = s.Sum(p => p.Charge)

};

foreach(var q in query)

{

Console.WriteLine("EIRNO : {0} Charge : {1} CNTNo : {2}",q.CNTNo,q.Charge,q.CNTNo);

}

结果:EIRNO : 1 Charge : 25 CNTNo : 1

EIRNO : 2 Charge : 20 CNTNo : 2

时间: 2024-10-08 17:34:42

第二十一章、使用查询表达式来查询内存中的数据的相关文章

从零开始的linux 第二十一章(Inode以及Block详解其二与软链接)

从零开始的linux 第二十一章 在紧张的学习中,又迎来了新的一周~~小编也在同学们的迫切要求下继续来更新博客咯~~ 同学们:"我们才没要求你!" 唉??同学们一点都不配合呢~~别不好意思嘛~~ 好啦~不逗你们了,小编就节省大家的时间,赶快开始这章的课程吧~~ 在上一章中,小编给同学们讲了 Inode号 和 Block,不过还没有给同学们讲完哦~所以上一章的题目是 其一 ,那么毫无疑问这一章就是其二的内容咯~从上一章同学们已经了解了什么是Inode和Block,以及Inode中包含哪些

2017.2.15 开涛shiro教程-第二十一章-授予身份与切换身份(一) table、entity、service、dao

原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 第二十一章 授予身份与切换身份(一) 1.使用场景 某个领导因为某些原因不能访问一些网站,他想把这个网站上的工作委托给秘书,但是他又不想提供账户.密码.此时可以使用shiro的 RunAs 功能. RunAs:允许一个用户假装为另一个用户(如果获得了允许)的身份进行访问. 注意,本章代码基于<第十六章 综合实例>,详细的数据模型及基本流程见该章. 2.表及数据

第二十一章

孔德之容,唯道是从.道之为物,唯望唯沕.沕呵望呵,中有象呵:望呵沕呵,中有物呵:幽呵冥呵,中有精呵:其精甚真,其中有信.自今及古,其名不去,以顺众父.吾何以知众父之然也?以此. “   孔德之容”,大德的这种运动“唯道是从”,它只是去追随道的运转,或者是按照道的这个运转规律去运转,所以老子在这讲出来道是德的内在动力,德是道的外在体现,大德,德行的这种运动完全是按照道的规律去运转的,叫唯道是从.您如果懂得了道的原理,您就能够从容的去做这个德了 沕是深远的意思,那个望是辽阔辽远的意思,没有边界,这个

C#高级编程第11版 - 第二十一章

导航 C# 全版本特性一览 全书目录 第二十一章 Tasks and Parallel Programming 21.1 概述 459 21.2 Parallel 类 460 21.2.1 使用Parallel.For()方法循环 460 21.2.2 提前中断Parallel.For 462 21.2.3 Parallel.For()方法的初始化 462 21.2.4 使用Parallel.ForEach()方法循环 463 21.2.5 通过Parallel.Invoke()方法调用多个方法

EL表达式无法显示Model中的数据

EL表达式无法显示Model中的数据 最近在学习SpringMVC,所有的配置都已经完成,但是在测试的时候EL表达式一直无法显示,例如存储在Model中${message},在jsp页面既然原样输出${message},EL表达式好像就无效, 那么问题来了,什么原因导致的不显示了,反复检查代码和配置,依然没有发现错误,代码如下: 首先是Spring Controller import org.springframework.web.servlet.ModelAndView;import org.

TCP/IP详解 卷1 第二十一章 TCP的超时与重传

21.1 引言 可靠性的保证之一就是超时重传 前面两个超时重传的例子 1)  ICMP端口不能到达时,TFTP客户使用UDP实现了一个简单的超时和重传机制,假定5s是一个适当是时间间隔,并每隔5s进行重传 2)  在向一个不存在的主机发送ARP的 例子中,可看到当TCP试图建立连接的时候,在每个重传之间使用一个较长的时延来重传SYN 对于每个连接,TCP管理4个不同的定时器: 1)  重传定时器使用于当希望收到另一端的确认 2)  坚持(persist)定时器使窗口大小信息保持不断流动,即使另一

第十一章 springboot + mongodb(简单查询)

1.mongodb在mac上的安装 下载mongodb,https://www.mongodb.org/ 解压缩到一个指定文件夹,如:/Users/enniu1/Desktop/zjg/mongodb-osx-x86_64-3.2.6(这是我的mongodb的版本) 配置PATH 输入命令:"vi ~/.bash_profile" 添加如下两句配置: 1 export MONGO_HOME=/Users/enniu1/Desktop/zjg/mongodb-osx-x86_64-3.2

第二十一章 命名空间和程序集

命名空间和程序集引用其他程序集mscorlib库命名空间命名空间名称命名空间的补充命名空间跨文件伸展嵌套命名空间using 指令using命名空间指令using别名指令程序集的结构程序集标识符强命名程序集创建强命名程序集程序集的私有方式部署共享程序集和GAC把程序集安装到GACGAC内的并肩执行配置文件延迟签名 命名空间和程序集 引用其他程序集 在第1章中,我们在高层次上观察了编译过程.编译器接受源代码文件并生称名称为程序集的输出文件.这一章中,我们将详细阐述程序集以及它们是如何生成和部署的.你

第二十一章 springboot + 定时任务

1.application.properties #cron job.everysecond.cron=0/1 * * * * * job.everytensecond.cron=0/10 * * * * * job.everyminute.cron=0 0/1 * * * * job.everysecond2.cron=* 0/1 * * * * 注意:cron表达式 第一个:每秒 第二个:每10秒 第三个:每分 第四个:每秒(注意这个不是每分) 2.CronJobTest.java 1 pa