11、LINQ表达式LINQ语言集成查询(LanguageIntegratedQuery)LINQ最明显的“语言集成”部分是查询表达式。查询表达式是使用C#3.0中引入的声明性查询语法编写的。通过使用查询语法,您甚至可以使用最少的代码对数据源执行复杂的筛选、排序和分组操作。您使用相同的基本查询表达式模式来查询和转换SQL数据库、ADO.NET数据集、XML文档和流以及.NET集合中的数据。下面的示例演示了完整的查询操作。完整操作包括创建数据源、定义查询表达式,以及在foreach语句中执行查询。ClassLINQQueryExpressions{staticvoidMain(){//Specifythedatasource.int[]scores=newint[]{97,92,81,60,89,45,34,78};//Definethequeryexpression.IEnumerableintscoreQuery=fromscoreinscoreswherescore80selectscore;//Executethequery.2foreach(intiinscoreQuery){Console.Write(i+);}}}//Output:9792818932、LINQ查询“查询”是指一组指令,这些指令描述要从一个或多个给定数据源检索的数据以及返回的数据应该使用的格式和组织形式。查询不同于它所产生的结果。通常,源数据会在逻辑上组织为相同种类的元素序列。SQL数据库表包含一个行序列。与此类似,ADO.NETDataTable包含一个DataRow对象序列。在XML文件中,有一个XML元素“序列”(不过这些元素按分层形式组织为树结构)。内存中的集合包含一个对象序列。从应用程序的角度来看,原始源数据的具体类型和结构并不重要。应用程序始终将源数据视为一个IEnumerable(Of(T))或IQueryable(Of(T))集合。在LINQtoSQL中,源数据显示为一个IEnumerableXElement。在LINQtoDataSet中,它是一个IEnumerableDataRow。在LINQtoSQL中,它是您定义用来表示SQL表中数据的任何自定义对象的IEnumerable或IQueryable。检索一个元素子集以产生一个新序列,但不修改单个元素。然后,查询可以按各种方式对返回的序列进行排序或分组,如下面的示例所示(假定scores是int[]):IEnumerableinthighScoresQuery=fromscoreinscoreswherescore804orderbyscoredescendingselectscore;如上一个示例所述检索一个元素序列,但是将这些元素转换为具有新类型的对象。例如,查询可以只从数据源中的某些客户记录检索姓氏。或者,查询可以检索完整的记录,再使用它构建另一个内存中对象类型甚至XML数据,然后生成最终的结果序列。下面的示例演示了从int到string的转换。请注意highScoresQuery的新类型。IEnumerablestringhighScoresQuery2=fromscoreinscoreswherescore80orderbyscoredescendingselectString.Format(Thescoreis{0},score);检索有关源数据的单一值。IEnumerableinthighScoresQuery3=fromscoreinscoreswherescore80selectscore;intscoreCount=highScoresQuery3.Count();53、查询表达式“查询表达式”是用查询语法表示的查询,是一流的语言构造。它就像任何其他表达式一样,并且可以用在C#表达式有效的任何上下文中。查询表达式由一组用类似于SQL或XQuery的声明性语法编写的子句组成。每个子句又包含一个或多个C#表达式,而这些表达式本身又可能是查询表达式或包含查询表达式。查询表达式必须以from子句开头,并且必须以select或group子句结尾。在第一个from子句和最后一个select或group子句之间,查询表达式可以包含一个或多个下列可选子句:where、orderby、join、let甚至附加的from子句。还可以使用into关键字使join或group子句的结果能够充当同一查询表达式中附加查询子句的源。3.1查询变量在LINQ中,查询变量是任何存储查询而不是查询结果的变量。更具体地说,查询变量始终是一个可枚举的类型,当在foreach语句中或对其IEnumerator.MoveNext方法的直接调用中循环访问它时,它将产生一个元素序列。3.2查询变量的显式类型化和隐式类型化6使用var关键字指示编译器在编译时推断查询变量(或任何其他本地变量)的类型。3.3开始查询表达式查询表达式必须以from子句开头。它同时指定了数据源和范围变量。在对源序列进行遍历的过程中,范围变量表示源序列中的每个后续元素。将根据数据源中元素的类型对范围变量进行强类型化。在使用分号或延续子句退出查询之前,范围变量将一直位于范围中。查询表达式可以包含多个from子句。当源序列中的每个元素本身就是集合或包含集合时,可使用附加的from子句。3.4结束查询表达式查询表达式必须以select子句或group子句结尾。group子句使用group子句可产生按照指定的键组织的组序列。键可以采用任何数据类型。select子句使用select子句可产生所有其他类型的序列。into延续可以在select或group子句中使用into关键字来创建用于存储查询的临时标识符。在下面的示例中,以一千万人口范围为界对countries进行分组。在创建7这些组之后,使用附加子句筛选掉某些组,然后按升序对剩下的组进行排序。若要执行这些附加操作,需要使用由countryGroup表示的延续。varpercentileQuery=fromcountryincountriesletpercentile=(int)country.Population/10000000groupcountrybypercentileintocountryGroupwherecountryGroup.Key=20orderbycountryGroup.KeyselectcountryGroup;3.5筛选、排序和联接在from开始子句以及select或group结束子句之间,所有其他子句(where、join、orderby、from、let)都是可选的。任何可选子句都可以在查询正文中使用零次或多次。where子句使用where子句可以根据一个或多个谓词表达式筛选掉源数据中的某些元素。orderby子句使用orderby子句可以按升序或降序对结果进行排序。您还可以指定次要排序顺序。join子句8使用join子句可以根据每个元素中指定键之间的相等比较,对一个数据源中的元素与另外一个数据源中的元素进行关联和/或组合。在LINQ中,联接操作是针对其元素具有不同类型的对象序列执行的。在联接两个序列之后,必须使用select或group语句指定要存储到输出序列中的元素。还可以使用匿名类型将每组关联元素中的属性组合为输出序列的新类型。let子句使用let子句可以将表达式(如方法调用)的结果存储到新的范围变量中。3.6查询表达式中的子查询查询子句本身可能包含一个查询表达式,该查询表达式有时称为“子查询”。每个子查询都以它自己的from子句开头,该子句不一定指向第一个from子句中的同一数据源。varqueryGroupMax=fromstudentinstudentsgroupstudentbystudent.GradeLevelintostudentGroupselectnew{Level=studentGroup.Key,HighestScore=(fromstudent2instudentGroupselectstudent2.Scores.Average()).Max()9};注意:在LINQ文档中,存储查询的变量在其名称中包含单词“query”,而存储实际结果的变量在其名称中不包含单词“query”。4、BT.SaaS.UI.WidgetFramework中LINQ的使用以BT.SaaS.UI.WidgetFramework.DataAccess的DatabaseHelper类为例:定义连接字符串publicconststringConnectionStringName=DashboardConnectionString;定义ApplicationIDpublicconststringApplicationID=10fd639154-299a-4a9d-b273-69dc28eb6388;publicreadonlystaticGuidApplicationGuid=newGuid(ApplicationID);定义最大行数,防止查询大量结果导致服务器阻塞publicconstintMAX_ROWS=1000;publicstaticDashboardDataContextGetDashboardData(){returnGetDashboardData(true,MAX_ROWS);}重载GetDashboardData()方法publicstaticDashboardDataContext2GetDashboardData(boolreadOnly,intmaxRowsAllowedToAffect){读取数据库连接配置vardb=newDashboardDataContext2(ConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString);设置事物隔离级别为READUNCOMMITTEDif(readOnly){11db.Connection.Open();db.ExecuteCommand(SETTRANSACTIONISOLATIONLEVELREADUNCOMMITTED;SETNOCOUNTON;SETROWCOUNT+maxRowsAllowedToAffect);}设置事物隔离级别,防止在提交事务之前记录受其他用户的影响,避免了幻觉读else{db.Connection.Open();db.ExecuteCommand(SETTRANSACTIONISOLATIONLEVELSERIALIZABLE;SETROWCOUNT+maxRowsAllowedToAffect);}返回db连接对象returndb;}更新操作publicstaticvoidUpdateT(Tobj,ActionTdetach,ActionTupdate)whereT:class12{using(vardb=GetDashboardData(false,1)){detach(obj);db.GetTableT().Attach(obj);update(obj);db.SubmitChanges();}}更新所有PublicstaticvoidUpdateAllT(ListTitems,ActionTdetach,ActionTupdate)whereT:class{using(vardb=GetDashboardData(false,items.Count)){TableTtable=db.GetTableT();foreach(Titeminitems){detach(item);table.Attach(item);update(item);13}db.SubmitChanges();}}删除操作publicstaticvoidDeleteT(Te