第13章 使用LINQ处理数据   .NET Language-Integrated Query(LINQ,语言集成查询)是微软公司提供的一项新技术,它将查询操作直接引入到.NET Framework 3.5所支持的编程语言(如C#、Visual Basic等)中,从而直接实现查询功能。本章将讲解使用LINQ处理的方法,主要介绍以下知识点: * LINQ概述; * LINQ查询子句; * 使用LINQ维护User表的数据。   通过本章的学习,读者将可以了解使用LINQ查询集合中的数据的基本操作方法,并使用LINQ处理数据库中的数据的方法。 13.1 LINQ概述   查询(Query)是一种从给定的数据源中检索满足指定条件的数据表达式。查询也是一组指令,这些指令可以从一个或多个给定的数据源中检索数据,并指定检索结果的数据类型和表现形式。   传统上,查询技术往往使用字符串来表示查询操作,如查询关系数据库的SQL语句、查询XML结构数据的XQuery等。在这些查询操作中,一般不会检查被查询数据的类型。同时,这些查询操作往往与编程语言处于一种相对孤立的状态。   LINQ(.NET Language-Integrated Query、语言集成查询)也是一种查询技术,它最大的特点就是能够把查询操作直接引入到.NET Framework 3.5所支持的编程语言(如C#、Visual Basic等)中,并整合为一体。LINQ查询表达式是由一组用类似于SQL或XQuery的声明性语法编写的子句组成。每一个子句可以包含一个或多个C#表达式,而这些表达式本身又可能是查询表达式或包含查询表达式。LINQ为.NET Framekwork 3.5所支持,它包括以下4个主要组件或技术。 * LINQ to Objects:查询IEnumerable或IEnumerable类型的集合,即查询任何可枚举的集合,如数组(Array和ArrayList)、泛型列表(List)、字典(Dictionary),以及用户自定义的集合。 * LINQ to SQL:查询和处理(如插入、修改、删除、排序等操作)基于关系数据库(如SQL Server数据库等)的数据。 * LINQ to DataSet:查询和处理DataSet对象中的数据,并这些数据进行检索、过滤和排序等操作。 * LINQ to XML:查询和处理XML结构的数据(如XML文档、XML数据片段、XML格式的字符串等)。   【示例13-1】 创建一个简单的LINQ查询表达式query0。该表达式查询sources数组中小于3的元素。 int[] sources = {0,1,2,3,4,5,6,7,8,9}; var query0 = from v in sources where v < 3 select v; 13.2 LINQ查询子句   LINQ查询表达式由一组类似于SQL或XQuery的声明性语法编写的子句组成。每一个子句可以包含一个或多个C#表达式,而这些表达式本身又可能是查询表达式或包含查询表达式。LINQ查询表达式包含8个基本子句,具体说明如下所述。 * from子句:指定查询表达式的数据源和范围变量。 * select子句:指定筛选元素的逻辑条件。一般由逻辑运算符组成。 * where子句:指定查询结果的表现形式。 * group子句:对查询结果进行分组。 * orderby子句:对查询结果进行排序。 * join子句:用来连接多个查询操作的数据源。 * let子句:可以用来引入用于存储子表达式查询结果的范围变量。 * into子句:提供一个临时标识符,使用该标识可以允许对join、group或select子句结果的引用。 13.2.1 from子句   from子句和SQL语句中的“FROM子句”比较相似,它可以指定查询的数据源(也包括子查询的数据源)和范围变量。其中,范围变量用来表示数据源序列中的每一个元素。 ?注意:LINQ查询表达式必须包含from子句,且以from子句开头。   【示例13-2】 创建了一个LINQ查询表达式,并保存为query1。该表达式从sources数组中查询小于2的元素。 ?注意:s为范围变量。sources为数据源,它为整型数组,并包含10个值。 int[] sources = {0,1,2,3,4,5,6,7,8,9}; var query1 = from s in sources where s < 2 select s;      示例13-2的查询结果如表13.1所示。 表13.1 示例13-2的查询结果表 数 据 源 查询表达式 查 询 结 果 sources(包含9个元素,分别为0、1、2、3、4、5、6、7、8、9) from s in sources where s < 2 select s; query1(包含两个元素,分别为0和1)   【示例13-3】 创建了一个LINQ查询表达式,并保存为query2。该表达式从strs字符串中查询a字符到g之间的字符。 ?注意:c为范围变量。strs为数据源,它为一个字符串,值为“This is a string.”。 string strs = "This is a string."; var query2 = from c in strs where c <= 'g' && c >= 'a' select c;      示例13-3的查询结果如表13.2所示。 表13.2 示例13-3的查询结果表 数 据 源 查询表达式 查 询 结 果 strs,值为“This is a string.” from c in strs where c <= 'g' && c >= 'a' select c; query2(包含两个元素,分别为“a”和“g”) ?注意:from子句指定数据源的类型必须为IEnumerable、IEnumerable或前两者的派生类型。   根据from子句的数量和组合方式,可以把LINQ查询表达式分为以下3种类型。   * 单个from子句查询表达式。   * 多个from子句查询表达式。   * 复合from子句查询表达式。   1.单个from子句查询   若LINQ查询表达式只包含一个from子句,则称该查询为单个from子句查询。一般情况下,单个from子句查询往往使用一个数据源。   【示例13-4】 创建了一个LINQ查询表达式,并保存为query3。该表达式从strs字符串中查询a字符到c之间的字符。    string strs = "This is a string."; var query3 = from c in strs where c <= 'c' && c >= 'a' select c;      示例13-4的查询结果如表13.3所示。 表13.3 示例13-4的查询结果表 数 据 源 查询表达式 查 询 结 果 strs,值为“This is a string.” from c in strs where c <= 'c' && c >= 'a' select c; query3(包含1个元素,为“a”)   2.多个from子句查询   若LINQ查询表达式包含多个from子句,则称该查询为多个from子句查询。一般情况下,包含多个from子句的查询往往使用多个数据源。   【示例13-5】 创建了一个LINQ查询表达式,并保存为query4。该表达式从sourcesa数组中查询小于2的元素,并从sourcesb数组中查询大于100的元素,并计算两个查询的和。    int[] sourcesa = {0,1,2,3,4,5,6,7,8,9}; int[] sourcesb = {100,200,300,400,2008}; var query4 = from s in sourcesa where s < 2 from i in sourcesb where i > 100 select s + i;      示例13-5的查询结果如表13.4所示。 表13.4 示例13-5的查询结果表 数 据 源 查询表达式 查 询 结 果 sourcesa(包含9个元素,分别为0、1、2、3、4、5、6、7、8、9) sourcesb(包括5个元素,分别为100、200、300、400、2008) from s in sourcesa where s < 2 from i in sourcesb where i > 100 select s + i; query4(包含8个元素,分别为200、300、400、2008、201、301、401、2009)      3.复合from子句查询   在一些情况下,数据源的元素也是一个数据源(被称为子数据源)。如果要查询子数据源中的元素,则需要使用复合from子句。   【示例13-6】 创建了一个LINQ查询表达式,并保存为query5。该表达式首先从sources字符数组中查询包含string的字符串(使用“from s in sources”子句),然后从该字符串中查询a字符到g之间的字符(使用“from c in s”子句)。最后,输出查询结果的字符及其所在的字符串。    string[] sources = {"This is a string.","This is a book."}; var query5 = from s in sources from c in s where s.IndexOf("string") >-1 && c <= 'g' && c >= 'a' select c + ":" + s;      示例13-6的查询结果如表13.5所示。 表13.5 示例13-6的查询结果表 数 据 源 查询表达式 查 询 结 果 sources(包含2个元素,分别为“This is a string.”和“This is a book.”) from s in sources from c in s where s.IndexOf("string") >-1 && c <= 'g' && c >= 'a' select c + ":" + s; query5(包含2个元素,分别为“a:This is a string.”和“g:This is a string.”) 13.2.2 where子句   where子句和SQL语句中的“WHERE子句”比较相似,它指定查询表达式的筛选元素满足的逻辑条件。where子句一般由逻辑运算符(如逻辑“与”、逻辑“或”)组成。一个查询表达式可以包含1个或多个where子句,甚至不包含where子句。每一个where子句可以包含1个或多个布尔条件表达式。 ?注意:对LINQ查询表达式而言,where子句不是必须的。如果LINQ查询表达式包含了where子句,那么where子句不能为LINQ查询表达式的第一个子句或最后一个 子句。   【示例13-7】 创建了一个LINQ查询表达式,并保存为query6。该表达式从sources数组中查询大于3的元素。    int[] sources = {0,1,2,3,4,5,6,7,8,9}; var query6 = from s in sources where s > 3 select s;      示例13-7的查询结果如表13.6所示。 表13.6 示例13-7的查询结果表 数 据 源 查询表达式 查 询 结 果 sources(包含9个元素,分别为0、1、2、3、4、5、6、7、8、9) from s in sources where s > 3 select s; query6(包含6个元素,分别为4、5、6、7、8、9)      【示例13-8】 创建了一个LINQ查询表达式,并保存为query7。该表达式从sources数组中查询大于3且小于8的元素。    int[] sources = {0,1,2,3,4,5,6,7,8,9}; var query7 = from s in sources where s > 3 && s < 8 select s;      示例13-8的查询结果如表13.7所示。 表13.7 示例13-8的查询结果表 数 据 源 查询表达式 查 询 结 果 sources(包含9个元素,分别为0、1、2、3、4、5、6、7、8、9) from s in sources where s > 3 && < 8 select s; query7(包含4个元素,分别为4、5、6、7) 13.2.3 select子句   select子句和SQL语句中的“SELECT子句”比较相似,它可以指定查询结果的类型和表现形式。一个LINQ查询表达式可以不包含select子句,也可以包含1个select子句。 ?注意:LINQ查询表达式要么以select子句结束,要么以group子句结束。   【示例13-9】 创建了一个LINQ查询表达式,并保存为query8。该表达式从sources数组中查询大于7的元素。    int[] sources = {0,1,2,3,4,5,6,7,8,9}; var query8 = from s in sources where s > 7 select s;      示例13-9的查询结果如表13.8所示。 表13.8 示例13-9的查询结果表 数 据 源 查询表达式 查 询 结 果 sources(包含9个元素,分别为0、1、2、3、4、5、6、7、8、9) from s in sources where s > 7 select s; query8(包含2个元素,分别为8和9)      【示例13-10】 创建了一个LINQ查询表达式,并保存为query9。该表达式从sources数组中查询小于3的元素,并返回查询结果的元素与100的乘积。    int[] sources = {0,1,2,3,4,5,6,7,8,9}; var query9 = from s in sources where s < 3 select s * 100;      示例13-10的查询结果如表13.9所示。 表13.9 示例13-10的查询结果表 数 据 源 查询表达式 查 询 结 果 sources(包含9个元素,分别为0、1、2、3、4、5、6、7、8、9) from s in sources where s < 3 select s * 100; query8(包含3个元素,分别为0、100、200)      【示例13-11】 创建了一个LINQ查询表达式,并保存为query10。该表达式从sources数组中查询小于3的元素,并返回新对象。其中,新对象的ID属性值为查询结果的元素值;Username属性的值则由“User”字符串和查询结果的元素的值组成。    int[] sources = {0,1,2,3,4,5,6,7,8,9}; var query10 = from s in sources where s < 3 select new { ID = s, Username = "User" + s.ToString() };      示例13-11的查询结果如表13.10所示。 表13.10 示例13-11的查询结果表 数 据 源 查询表达式 查 询 结 果 sources(包含9个元素,分别为0、1、2、3、4、5、6、7、8、9) from s in sources where s < 3 select new UserInfo { ID = s, Username = "User" + s.ToString() };; query10(包含3个元素,元素的ID属性的值分别为0、1、2) 13.2.4 group子句   group子句可以对查询的结果进行分组,并返回元素类型为IGrouping的对象序列。其中,TKey参数指定元素的键的类型,TElement参数指定元素的值的类型。 ?注意:IGrouping和IEnumerable非常相似,在此不做详细介绍。   【示例13-12】 创建了一个LINQ查询表达式,并保存为query11。该表达式从sources数组中查询元素,并按照元素的奇偶性进行分组。    int[] sources = {0,1,2,3,4,5,6,7,8,9}; var query11 = from s in sources group s by s % 2 == 0;      【示例13-13】 使用2个foreach语句输出了query11查询的结果。第1个foreach语句枚举query11查询结果中的所有的组,第2个foreach语句枚举每一个组中的每一个元素。    foreach(var q in query11) { foreach(var subq in q) { Response.Write(subq.ToString() + ","); } Response.Write("
"); } 13.2.5 orderby子句   orderby子句可以按照关键字(或键)对查询结果进行排序,其中,排序方式可以为“升序”或“降序”,排序的关键字(或键)可以为一个或多个。 ?注意:order子句的默认排序方式为“升序”。   【示例13-14】 创建了一个LINQ查询表达式,并保存为query12。该表达式从sources数组中查询大于7的元素,并按照元素进行升序排序。    int[] sources = {0,1,2,3,4,5,6,7,8,9}; var query12 = from s in sources where s > 7 order by s select s;   示例13-14的查询结果如表13.11所示。 表13.11 示例13-14的查询结果表 数 据 源 查询表达式 查 询 结 果 sources(包含9个元素,分别为0、1、2、3、4、5、6、7、8、9) from s in sources where s > 7 order by s select s; query12(包含2个元素,分别为8和9)      【示例13-15】 创建了一个LINQ查询表达式,并保存为query13。该表达式从sources数组中查询元素,并按照元素的奇偶性进行分组。其中,每一个分组都按照元素的值进行倒序排序。    int[] sources = {0,1,2,3,4,5,6,7,8,9}; var query13 = from s in sources orderby s descending group s by s % 2 == 0; 13.2.6 into子句   into子句可以创建一个临时标识符,使用该标识符可以存储group、join或select子句查询结果。   【示例13-16】 创建了一个LINQ查询表达式,并保存为query14。该查询表达式的具体操作说明如下所述。 * 从sources数组中查询元素。 * 按照元素的奇偶性进行分组。 * 每一个分组中的元素都按照元素的值进行倒序排序。 * 将每一个查询结果临时保存为g变量。 * 使用where子句判断查询g变量中的元素数量是否大于5,如果大于5,则选择该组。    int[] sources = {0,1,2,3,4,5,6,7,8,9,10}; var query14 = from s in sources orderby s descending group s by s % 2 == 0 into g where g.Count() > 5 select g;      示例13-16的查询结果如表13.12所示。 表13.12 示例13-16的查询结果表 数 据 源 查询表达式 查 询 结 果 sources(包含10个元素,分别为0、1、2、3、4、5、6、7、8、9、10) from s in sources orderby s descending group s by s % 2 == 0 into g where g.Count() > 5 select g; query14(包含1个元素,该元素为一个组,组内包含6个元素,分别为10、8、6、4、2、0) 13.2.7 let子句   let子句可以创建一个范围变量,使用该变量可以保存表达式中的中间结果。 ?注意:let子句创建的范围变量的值只能通过初始化操作进行赋值。范围变量的值一旦被初始化,将不能再被改变。   【示例13-17】 创建了一个LINQ查询表达式,并保存为query15。该查询表达式的具体操作说明如下所述。 * 从sources数组中查询元素。 * 使用let语句创建范围变量m,它的值为元素除以2的余数。 * 查询元素的值大于3,且m变量的值等于1的元素。    int[] sources = {0,1,2,3,4,5,6,7,8,9}; var query15 = from s in sources let m = s % 2 where s > 3 && m == 1 select s;      示例13-17的查询结果如表13.13所示。 表13.13 示例13-17的查询结果表 数 据 源 查询表达式 查 询 结 果 sources(包含9个元素,分别为0、1、2、3、4、5、6、7、8、9) from s in sources let m = s % 2 where s > 3 && m == 1 select s; query15(包含3个元素,分别为5、7和9) 13.2.8 join子句   join子句可以设置两个或多个存在相关属性或值的数据源之间的关系。join子句可以为数据源之间建立以下3种联接关系。 * 内部联接,和SQL语句中的“INNER JOIN子句”比较相似,两个数据源都必须存在相同的值,即两个数据源都必须存在满足联接关系的元素。 * 分组联接,即为含有into子句的join子句。 * 左外部联接,和SQL语句中的“INNER LEFT子句”比较相似。   【示例13-18】 创建两个类:UserInfo和Role- Info。UserInfo类描述用户的信息,包含3个属性:ID、RoleID和Username。RoleInfo类描述角色的信息,包含2个属性:ID和RoleName。UserInfo和RoleInfo类的类关系图如图13.1所示。 ?注意:UserInfo类的RoleID属性的值是引用于RoleInfo类的ID属性。因此,UserInfo类的RoleID属性和RoleInfo类的ID属性将建立联接关系。 /// 角色的基本信息 public class RoleInfo { private int id; private string roleName; /// 角色的ID值 public int ID{get { return id; }set { id = value; }} /// 角色的名称 public string RoleName{get { return roleName; }set { roleName = value; }} } /// 用户的基本信息 public class UserInfo { private int id; private string username; private int roleID; /// 用户的ID值 public int ID{get { return id; }set { id = value; }} /// 用户的名称 public string Username{get { return username; }set { username = value; }} /// 用户的角色ID值 public int RoleID{get { return roleID; }set { roleID = value; }} }      【示例13-19】 创建两个泛型集合:users和roles,它们的类型分别为List和List。使用foreach语句初始化users和roles集合,并都添加了9个元素。    ///构建数据源 System.Collections.Generic.List users = new System.Collections. Generic.List(); System.Collections.Generic.List roles = new System.Collections. Generic.List(); for(int i = 1; i < 10; i++) { ///创建UserInfo类的实例 UserInfo ui = new UserInfo(); ui.ID = i; ui.RoleID = i * 2; ui.Username = "User0" + i.ToString(); users.Add(ui); ///创建RoleInfo类的实例 RoleInfo ri = new RoleInfo(); ri.ID = i; ri.RoleName = "Role0" + i.ToString(); roles.Add(ri); }   1.内部联接   内部联接要求两个数据源都必须满足联接关系的元素。如果不存在满足联接关系的元素,则两个数据源的内部联接之后,数据源的元素数量为空,即联接之后的数据源为空。   【示例13-20】 使用join子句联接了users和roles数据源,联接关系为“相等(equal)”,并查询ID属性值小于9的元素。 ///查询ID值小于9,且角色包含在roles中的用户 var query16 = from u in users where u.ID < 9 join r in roles on u.RoleID equals r.ID select u;   2.分组联接   分组联接是指包含into子句的join子句的联接。分组联接产生分层结构的数据,它将第一个数据源中的每个元素与第二个数据源中的一组相关元素进行匹配。第一个数据源中的元素都会出现在查询结果中。如果第一个数据源中的元素在第二个数据源中找到相关元素,则使用被找到的元素,否则使用空。   【示例13-21】 使用join子句联接了users和roles数据源,联接关系为“相等(equal)”,查询操作具体说明如下: * 从users数据源中查询元素。 * 使用join子句联接了users和roles数据源,联接关系为“相等(equal)”,并设置组的标识为g。 * 使用select子句查询一个新类型的数据。其中,Roles属性的值为分组g的值(为一个集合)。    ///查询ID值小于9,且角色包含在roles中的用户 var query17 = from u in users where u.ID < 9 join r in roles on u.RoleID equals r.ID into g select new { ID = u.ID, Username = u.Username, RoleID = u.RoleID, Roles = g.ToList() };   3.左外部联接   左外部联接和SQL语句中的LEFT JOIN比较相似,它将返回第1个数据源中的每一个元素,而无论第2个数据源中是否存在相对应的相关元素。   【示例13-22】 使用join子句联接了users和roles数据源,联接关系为“相等(equal)”,查询操作具体说明如下: * 从users数据源中查询元素。 * 使用join子句联接了users和roles数据源,联接关系为“相等(equal)”,并设置组的标识为g。 * 使用from子句选择分组g中的默认元素。 * 使用select子句查询一个新类型的数据。其中,Roles属性的值为分组g的值(为一个集合)。    ///查询ID值小于9,且角色包含在roles中的用户 var query17 = from u in users where u.ID < 9 join r in roles on u.RoleID equals r.ID into g from ur in g.DefaultIfEmpty() select new { ID = u.ID, Username = u.Username, RoleID = u.RoleID, Roles = g.ToList() }; 13.3 使用LINQ实现用户管理模块   本章使用LINQ实现用户管理模块,它包括4个功能:查询用户、添加新用户、修改用户和删除用户,它们分别由SelectUser.aspx、AddUser.aspx、UpdateUser.aspx和DeleteUser.aspx页面实现。 13.3.1 数据库实体类设计   下面介绍为FirstSQLServerDB数据库创建数据库实体类的方法,即为该数据库创建一个名称为FirstSQLServerDB.dbml的DBML文件。具体步骤如下所述。   (1)右击“解决方案资源管理器”面板中的App_Code分支,并选择“添加新项”命令,弹出“添加新项-D:\BookCode\ASP.NET3.0Prime\Sample_13\”对话框,如图13.2所示。 图13.2 “添加新项-D:\BookCode\ASP.NET3.0Prime\Sample_13\”对话框   (2)在“名称”文本框中输入“FirstSQLServerDB.dbml”。   (3)单击“添加”按钮,关闭“添加新项-D:\BookCode\ASP.NET3.0Prime\Sample_13\”对话框,并创建FirstSQLServerDB.dbml文件。   (4)打开“服务器资源管理器”面板,并找到指示FirstSQLServerDB数据库的分支。   (5)展开该分支至“表”和“存储过程”分支,如图13.3所示。   (6)将FirstSQLServerDB数据库的所有表和存储过程直接拖放到FirstSQLServerDB.dbml文件的视图面板中,如图13.4所示。保存该文件即可创建FirstSQLServerDB.dbml文件。 图13.3 “服务器资源管理器”面板 图13.4 FirstSQLServerDB.dbml文件的视图 ?注意:创建FirstSQLServerDB.dbml文件之后,在“解决方案资源管理器”面板中的App_ Code系统文件中打开其代码设计文件FirstSQLServerDB.designer.cs文件,并添加新的命名空间“Sample_13”。 13.3.2 使用LinqDataSource控件访问数据库实体类   【实例13-1】 模块中的ShowUser.aspx页面使用LinqDataSource控件访问数据库实体类中的User表,并为GridView控件提供数据。最终,ShowUser.aspx页面显示User表中的记录,具体步骤如下所述。   (1)在Visual Studio 2008集成开发环境中,打开名称为“Sample_13”的ASP.NET网站。   (2)右击“解决资源方案管理器”面板中的“D:\...\Samle_13”节点,弹出“添加新项-D:\BookCode\ASP.NET3.0Prime\ch13\Sample_13\”对话框。选中“Web 窗体”图标,并在“名称”文本框中输入“ShowUser.aspx”。单击“添加”按钮,可以将ShowUser.aspx页面添加到Sample_13网站。   (3)打开SelectUser.aspx页面的“源”视图,并设置该页面的标题为“使用LinqDataSource控件访问数据库实体类”。   (4)在ShowUser.aspx页面声明一个名称为gvUser的GridView控件。下面介绍为gvUser控件配置名称为linqDSUser数据源的方法,具体步骤如下所述。 * 单击gvUser控件的<按钮,显示智能提示菜单,如图13.5所示。 * 选择“选择数据源”下拉列表框中的“新建数据源”列表项,打开“选择数据源类型”对话框。选择“LINQ”图标,并设置数据源的ID值为linqDSUser,如图13.6所示。 * 单击“确定”按钮,打开“选择上下文对象”对话框。在其中选择“FirstSQLServer- DBDataContext”列表项,如图13.7所示。 * 单击“下一步”按钮,打开“配置数据选择”对话框。选择User表的ID、Username、Email和Status列,如图13.8所示。 * 单击“完成”按钮,即可以配置好linqDSUser数据源。 图13.5 gvUser控件的智能提示 图13.6 “选择数据源类型”对话框 图13.7 “选择上下文对象”对话框 图13.8 “配置数据选择”对话框   综上所述,ShowUser.aspx页面的HTML设计代码如下:    <%@ Page Language="C#" %> 使用LinqDataSource控件访问数据库实体类
     (5)把ShowUser.aspx页面设置为Sample_13网站的起始页面,并运行该网站。在IE浏览器中查看ShowUser.aspx页面,如图13.9所示。此时,该页面显示User表中的记录。 图13.9 ShowUser.aspx页面显示User表中的记录 13.3.3 查询用户   【实例13-2】 模块中的SelectUser.aspx页面使用LINQ实现查询用户功能。该页面使用Grid- View控件显示LINQ查询表达式的查询结果,具体步骤如下所述。   (1)在Visual Studio 2008集成开发环境中,打开名称为Sample_13的ASP.NET网站。   (2)右击“解决资源方案管理器”面板中的“D:\...\Samle_13”节点,弹出“添加新项-D:\BookCode\ASP.NET3.0Prime\ch13\Sample_13\”对话框。选中“Web 窗体”图标,并在“名称”文本框中输入“SelectUser.aspx”。单击“添加”按钮,可以将SelectUser.aspx页面添加到Sample_13网站。   (3)打开SelectUser.aspx页面的“源”视图,并设置该页面的标题为“查询用户”。   (4)向SelectUser.aspx页面添加1个GridView控件,ID属性的值为gvUser。SelectUser.aspx页面的HTML设计代码如下:    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="SelectUser.aspx.cs" Inherits="SelectUser" %> 查询用户
     (5)打开SelectUser.aspx页面的代码隐藏文件,并创建SelectData()函数。该函数从User表中获取数据,具体步骤如下所述。 * 创建FirstSQLServerDB数据库的上下文对象db。 * 创建“from u in db.User select u;”LINQ查询表达式查询User表的所有记录,并保存为query变量。 * 将query设置为gvUser控件的数据源,并绑定控件数据,即在页面上显示User表的记录。   综上所述,SelectData()函数的程序代码如下:    private void SelectData() { ///创建FirstSQLServerDB数据库的上下文对象 Sample_13.FirstSQLServerDBDataContext db = new Sample_13. FirstSQLServerDBDataContext( ConfigurationManager.ConnectionStrings["FirstSQLServerDBConnec- tionString"].ConnectionString); ///查询User表的数据 var query = from u in db.User select u; ///绑定并显示数据 gvUser.DataSource = query; gvUser.DataBind(); }      (6)把SelectUser.aspx页面设置为Sample_13网站的起始页面,并运行该网站。在IE浏览器中查看SelectUser.aspx页面,如图13.10所示。此时,该页面显示User表中的记录。 图13.10 SelectUser.aspx页面显示User表中的记录 13.3.4 添加新用户   【实例13-3】 模块中的AddUser.aspx页面使用LINQ实现添加新用户的功能,并使用GridView控件显示LINQ查询表达式的查询结果。具体步骤如下所述。   (1)在Visual Studio 2008集成开发环境中,打开名称为Sample_13的ASP.NET网站。   (2)右击“解决资源方案管理器”面板中的“D:\...\Samle_13”节点,弹出“添加新项-D:\BookCode\ASP.NET3.0Prime\ch13\Sample_13\”对话框。选中“Web 窗体”图标,并在“名称”文本框中输入“AddUser.aspx”。单击“添加”按钮,可以将AddUser.aspx页面添加到Sample_13网站。   (3)打开AddUser.aspx页面的“源”视图,并设置该页面的标题为“添加新用户”。   (4)向AddUser.aspx页面添加1个GridView控件和1个Button控件,ID属性的值分别为gvUser和btnCommit。AddUser.aspx页面的HTML设计代码如下:    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="AddUser.aspx.cs" Inherits="AddUser" %> 添加新用户
     (5)打开AddUser.aspx页面的代码隐藏文件,并设计btnCommit控件的Click事件:btnCommit_Click(object sender,EventArgs e)。该事件使用LINQ实现向User表添加新用户的功能,具体步骤如下所述。 * 创建FirstSQLServerDB数据库的上下文对象db。 * 创建User类的实例user,并设置该实例的Usernam、Email、Status和Password属性的值。 * 调用db对象的InsertOnSubmit()方法将user实例插入到User表中。 * 调用db对象的SubmitChanges()方法将上述修改提交到数据库中。 * 调用SelectData()方法在gvUser控件中显示User表中的记录。   综上所述,btnCommit_Click(object sender,EventArgs e)事件的程序代码如下:    protected void btnCommit_Click(object sender,EventArgs e) { ///创建FirstSQLServerDB数据库的上下文对象 Sample_13.FirstSQLServerDBDataContext db = new Sample_13. FirstSQLServerDBDataContext( ConfigurationManager.ConnectionStrings["FirstSQLServerDBConnec- tionString"].ConnectionString); ///创建User类的实例,并设置属性的值 Sample_13.User user = new Sample_13.User(); user.Username = "User" + DateTime.Now.ToLongTimeString(). Replace(":",""); user.Email = user.Username + "@web.com"; user.Status = (byte)0; user.Password = user.Username; db.User.InsertOnSubmit(user); ///插入到User表 db.SubmitChanges(); ///将修改提交到数据库 SelectData(); ///重新显示数据 }      (6)AddUser.aspx页面使用SelectData()函数在gvUser控件中显示User表中的记录。该函数的具体实现已经在13.3.3节中介绍,在此不再介绍。   (7)把AddUser.aspx页面设置为Sample_13网站的起始页面,并运行该网站。在IE浏览器中查看AddUser.aspx页面,如图13.11所示。单击“单击一次,添加一个新用户”按钮向User表插入一条记录。此时,AddUser.aspx页面如图13.12所示。 13.3.5 修改用户   【实例13-4】 模块中的UpdateUser.aspx页面使用LINQ实现修改用户的功能,并使用GridView控件显示LINQ查询表达式的查询结果。具体步骤如下所述。   (1)在Visual Studio 2008集成开发环境中,打开名称为Sample_13的ASP.NET网站。   (2)右击“解决资源方案管理器”面板中的“D:\...\Samle_13”节点,弹出“添加新项-D:\BookCode\ASP.NET3.0Prime\ch13\Sample_13\”对话框。选中“Web 窗体”图标,并在“名称”文本框中输入“UpdateUser.aspx”。单击“添加”按钮,可以将UpdateUser.aspx页面添加到Sample_13网站。 ?图13.11 显示添加新用户前的User表中的记录 图13.12 显示添加新用户后的User表中的记录   (3)打开UpdateUser.aspx页面的“源”视图,并设置该页面的标题为“修改用户”。   (4)向UpdateUser.aspx页面添加1个GridView控件和1个Button控件,ID属性的值分别为gvUser和btnCommit。UpdateUser.aspx页面的HTML设计代码如下:    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="UpdateUser.aspx.cs" Inherits="UpdateUser" %> 修改用户
     (5)打开UpdateUser.aspx页面的代码隐藏文件,并设计btnCommit控件的Click事件btnCommit_Click(object sender,EventArgs e)。该事件使用LINQ实现修改User表中ID值等于5的记录的Username列的值的功能,具体步骤如下所述。 * 创建FirstSQLServerDB数据库的上下文对象db。 * 创建“from u in db.User where u.ID == 5 select u;”LINQ查询表达式查询被修改的记录,并保存为query。 * 使用foreach语句修改query遍历中的记录的Username属性值。 * 调用db对象的SubmitChanges()方法将上述修改提交到数据库中。   * 调用SelectData()方法在gvUser控件中显示User表中的记录。   综上所述,btnCommit_Click(object sender,EventArgs e)事件的程序代码如下:    protected void btnCommit_Click(object sender,EventArgs e) { ///创建FirstSQLServerDB数据库的上下文对象 Sample_13.FirstSQLServerDBDataContext db = new Sample_13. FirstSQLServerDBDataContext( ConfigurationManager.ConnectionStrings["FirstSQLServerDBConnec- tionString"].ConnectionString); ///查询被修改的用户 var query = from u in db.User where u.ID == 5 select u; ///修改用户的名称 foreach(var u in query){u.Username = "User" + DateTime.Now. ToLongTimeString().Replace(":","");} db.SubmitChanges(); ///将修改提交到数据库 SelectData(); ///重新显示数据 }      (6)UpdateUser.aspx页面使用SelectData()函数在gvUser控件中显示User表中的记录。该函数的具体实现已经在13.3.3小节中介绍,在此不再介绍。   (7)把UpdateUser.aspx页面设置为Sample_13网站的起始页面,并运行该网站。在IE浏览器中查看UpdateUser.aspx页面,如图13.13所示。单击“修改ID值等于5的用户”按钮,修改User表中ID值等于5的Username列的值。此时,UpdateUser.aspx页面如图13.14所示。 图13.13 显示修改用户前的User表中的记录 图13.14 显示修改用户后的User表中的记录 13.3.6 删除用户   【实例13-5】 模块中的DeleteUser.aspx页面使用LINQ实现删除用户的功能,并使用GridView控件显示LINQ查询表达式的查询结果。具体步骤如下所述。   (1)在Visual Studio 2008集成开发环境中,打开名称为Sample_13的ASP.NET网站。   (2)右击“解决资源方案管理器”面板中的“D:\...\Samle_13”节点,弹出“添加新项-D:\BookCode\ASP.NET3.0Prime\ch13\Sample_13\”对话框。选中“Web 窗体”图标,并在“名称”文本框中输入“DeleteUser.aspx”。单击“添加”按钮,可以将DeleteUser.aspx页面添加到Sample_13网站。   (3)打开DeleteUser.aspx页面的“源”视图,并设置该页面的标题为“删除用户”。   (4)向DeleteUser.aspx页面添加1个GridView控件和1个Button控件,ID属性的值分别为gvUser和btnCommit。DeleteUser.aspx页面的HTML设计代码如下:    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="DeleteUser.aspx.cs" Inherits="DeleteUser" %> 删除用户
     (5)打开DeleteUser.aspx页面的代码隐藏文件,并设计btnCommit控件的Click事件btnCommit_Click(object sender,EventArgs e)。该事件使用LINQ实现删除User表中ID值大于5的记录的功能,具体步骤如下所述。 * 创建FirstSQLServerDB数据库的上下文对象db。 * 创建“from u in db.User where u.ID > 5 select u;”LINQ查询表达式查询被删除的记录,并保存为query。 * 调用db对象的DeleteAllOnSubmit()方法从User表中删除query查询结构中的用户。 * 调用db对象的SubmitChanges()方法将上述修改提交到数据库中。 * 调用SelectData()方法在gvUser控件中显示User表中的记录。   综上所述,btnCommit_Click(object sender,EventArgs e)事件的程序代码如下:    protected void btnCommit_Click(object sender,EventArgs e) { ///创建FirstSQLServerDB数据库的上下文对象 Sample_13.FirstSQLServerDBDataContext db = new Sample_13. FirstSQLServerDBDataContext( ConfigurationManager.ConnectionStrings["FirstSQLServerDBConnec- tionString"].ConnectionString); ///查询被删除的用户 var query = from u in db.User where u.ID > 5 select u; db.User.DeleteAllOnSubmit(query); ///删除指定的User表 db.SubmitChanges(); ///将修改提交到数据库 SelectData(); ///重新显示数据 }      (6)DeleteUser.aspx页面使用SelectData()函数在gvUser控件中显示User表中的记录。该函数的具体实现已经在13.3.3节中介绍,在此不再介绍。   (7)把DeleteUser.aspx页面设置为Sample_13网站的起始页面,并运行该网站。在IE浏览器中查看DeleteUser.aspx页面,如图13.15所示。单击“删除ID值大于5的用户”按钮删除User表中ID值大于5的用户。此时,DeleteUser.aspx页面,如图13.16所示。 ? 图13.15 显示删除用户前的User表中的记录 ?图13.16 显示删除用户后的User表中的记录 13.4 上 机 实 践   1.代码调试   在Visual Studio 2008集成开发环境中调试下列代码是否能够正常运行。如果能够正常运行,请写出运行结果;如果不能正常运行,请指出错误代码,并改正。    <%@ Page Language="C#" %> 使用LinqDataSource控件访问数据库实体类
  2.编程题   在Visual Studio 2008集成开发环境中创建名称为“UserLogin”的ASP.NET网站,并在Default.aspx页面中使用LINQ实现用户登录(根据用户的名称和密码获取该用户的登录信息,如ID值等)功能。 13.5 常见问题及解答   问题1:使用LINQ时,无法连接到数据库,这是为什么?   解答:确保连接字符串是正确的,以及SQL Server实例正在运行。特别地,LINQ要求启用命名管道协议。   问题2:使用LINQ修改了数据库中的数据,但是在重新运行应用程序时更改已丢失。   解答:确保调用了SubmitChanges()方法将修改后的结果保存到数据库。 13.6 小 结   本章主要介绍了LINQ的基础知识,如LINQ概述、8个LINQ查询子句的基本语法、8个LINQ查询子句的使用方法。最后,重点介绍了使用LINQ实现用户管理模块的方法。该模块包括4个功能:查询用户、添加新用户、修改用户和删除用户。其中,读者要着重掌握LINQ基础知识。只有这样,才能使用LINQ处理快捷处理网站的数据,并简化后续开发工作。第14章将要介绍ASP.NET验证技术。 13.7 习 题   在Visual Studio 2008集成开发环境中创建名称为LinqUserManage的ASP.NET网站,并在该网站中实现以下功能。其中,该网站使用FirstSQLServerDB数据库。   (1)使用LINQ获取User表中以A字符开头的所有用户。   (2)使用LINQ获取User表中所有用户,并按照Status列进行分组。   (3)使用LINQ根据用户的名称和电子邮件获取该用户密码的功能。   (4)使用LINQ实现注册新用户的功能。   (5)使用LINQ实现修改用户密码的功能。   (6)创建名称为Pr_GetUserLogin的存储过程,并使用LINQ调用存储过程实现用户登录功能。 第3篇 ASP.NET数据处理篇    第13章 使用LINQ处理数据    ·300·       ·319·