第13章 语言集成查询LINQ
语言集成查询LINQ(Language Integrated Query)是.NET的特性,它提供了统一的语法来查询多种异构数据源(指不同数据库管理系统之间的数据)。开发人员可以使用任何.NET语言去查询数据源中的数据,而不用理会异构数据源之间的差异。本章我们来学习LINQ查询。
13.1 LINQ基础知识
LINQ是一组用于C#和Visual Basic语言的扩展指令,它允许用户编写C#或者Visual Basic代码,以与查询数据库相同的方式操作内存数据。本节我们来学习LINQ的基础知识。
13.1.1 LINQ简介
知识点讲解:光盘\视频讲解\第13章\LINQ简介.wmv
LINQ的核心是查询,通过它可以直接使用代码查询多种数据源中的数据。LINQ与.NET的关系如同SQL与关系数据库的关系。通过简单的声明性语法,可以查询集合中条件匹配的对象。本节我们来简单了解LINQ。
1. LINQ技术
LINQ技术主要包含三种分支,如图13.1所示。

图13.1 LINQ技术三大分支
对LINQ技术分支的具体说明如下。
- LINQ To Objects:主要用于操作内存中的集合数据,如查询集合中的数据对象。
- LINQ Enabled ADO.NET:主要针对关系型数据库中的对象-关系模型的数据查询,目前只支持SQL Server数据库中的数据。该技术包含三个分支,分别是LINQ To SQL、LINQ To DataSet和LINQ To Entities。
- LINQ To XML:主要针对XML结构的数据查询,目前支持各种XML文档的解析和查询工作。
2. LINQ表达式
LINQ技术包含多个分支,都使用LINQ表达式对数据进行查询。该表达式即LINQ查询的基本形式,主要包含三个部分。
- 查询数据源:任何LINQ查询都需要定制一个确切的数据源,该数据源可以是实现了IEnumerable<T>或Iqueryable<T>的集合。
- 查询句法:包括对查询数据的描述或者查询数据的上下文。
- 查询结果的呈现形式:LINQ可以动态指定查询结果的呈现形式。
注意:LINQ查询表达式的风格类似于SQL,它可以在任何有效的C#上下文中使用,并且具有一切表达式应有的特征。
13.1.2 简单查询
知识点讲解:光盘\视频讲解\第13章\简单查询.wmv
LINQ查询表达式必须以from子句开头,以select或group子句结尾。这些子句之间可以包含一个或多个可选的子句,如where子句、orderby子句等。select子句用于确定查询列举的值的类型,group子句用于分组返回数据,其中每个组都能够列举。where子句用于指定某种条件,查询后只会返回满足该条件的项。
LINQ查询的一般格式如图13.2所示。

图13.2 查询的一般格式
注意:在from子句中,变量将接收从数据源获得的元素。在where子句中,条件表达式必须产生布尔结果。在select子句中指定了查询获得的内容,要以“;”结尾。
【示例13-1】下面演示简单LINQ查询与传统查询的比较。
(1)创建一个网站,命名为“LINQ查询示例”。
(2)在该网站上添加一个Web页面,命名为Default.aspx。在该Web页面上添加两个Button控件和两个Label控件,并为两个Button控件添加单击事件。Default.aspx页面的代码如下:
示例13-1:一个简单的LINQ查询与传统查询
源码路径:光盘\源文件\Chapter13\示例13-1\LINQ查询示例\Default.aspx

在Default.aspx的代码后置文件中编写Button控件的单击事件,以实现查询的功能。Button1控件的单击事件使用LINQ查询语句,Button2控件的单击事件使用传统的查询方法,两个Button控件查询条件相同,因此查询结果也应该相同。Default.aspx文件的后置代码具体如下:
示例13-1:一个简单的LINQ查询与传统查询
源码路径:光盘\源文件\Chapter13\示例13-1\LINQ查询示例\Default.aspx.cs

运行结果如图13.3~图13.5所示。
![]() |
![]() |
![]() |
图13.3 运行结果图(1) | 图13.4 运行结果图(2) | 图13.5 运行结果图(3) |
在该示例中,两个按钮的单击事件产生了相同的输出结果。我们可以看到,传统的查询语法直接遍历并判断指定的a数组的元素是否大于10,而LINQ查询则使用了一种类似于SQL查询的语法,从a数组中查询,将结果保存在result变量中,再将result中的结果输出到Label控件上。
注意:var关键字是隐型局部变量声明符,它允许开发人员建立一个变量,且不必明确它的类型。该变量类型可以从初始化它的表达式中推导出来。
13.1.3 多次查询
知识点讲解:光盘\视频讲解\第13章\多次查询.wmv
查询用于检索数据的一组规则,但是自身不产生结果,因此相同的查询可以多次运行。如果数据源在多次运行期间发生改变,那么查询的结果就可能不同。
注意:在执行查询后,始终获得最新的结果。
【示例13-2】下面演示使用LINQ执行多次查询的方法。
(1)创建一个网站,命名为“LINQ多次查询示例”。
(2)在该网站上添加一个Web页面,命名为Default.aspx。在该Web页面上添加一个Button控件和一个Label控件,并为Button控件添加单击事件。Default.aspx页面的代码如下:
示例13-2:使用LINQ多次执行查询
源码路径:光盘\源文件\Chapter13\示例13-2\LINQ多次查询示例\Default.aspx

在Default.aspx的代码后置文件中编写Button控件的单击事件,以实现查询的功能。Button控件的单击事件使用LINQ查询语句,对数组num进行查询,并将三次查询结果分别显示在Label控件上。Default.aspx文件的后置代码具体如下:
示例13-2:使用LINQ多次执行查询
源码路径:光盘\源文件\Chapter13\示例13-2\LINQ多次查询示例\Default.aspx.cs

运行结果如图13.6所示。

图13.6 运行结果图
在该示例中,当num[0]元素和num[1]元素的值发生改变后,重新执行查询时结果就会发生变化。这就是LINQ查询的优点。
13.2 查询表达式中的上下文关键字
LINQ查询表达式内置了多种查询运算关键字,如from、where和orderby等。本节将为大家详细介绍这些查询表达式中的上下文关键字。
13.2.1 简介
知识点讲解:光盘\视频讲解\第13章\简介.wmv
查询表达式中使用的上下文关键字如表13.1所示。
表13.1 查询表达式中使用的上下文关键字

其中,查询子句的关键字如表13.2所示。
表13.2 查询子句的上下文关键字
from | where | select |
group | orderby | join |
let |
注意:查询子句的上下文关键字主要有7个。
13.2.2 使用where子句筛选数据
知识点讲解:光盘\视频讲解\第13章\使用where子句筛选数据.wmv
where子句用来筛选查询返回的数据。示例13-1和示例13-2中都只使用了一条查询语句,它们也可以使用多条where子句。使用多条where子句筛选结果的格式如图13.7所示。

图13.7 使用多条where子句筛选结果
还可以把多条where子句用“&&”符号合并成一条where子句,格式如图13.8所示。

图13.8 多条where子句
【示例13-3】下面演示如何使用where子句筛选查询结果。
(1)创建一个网站,命名为“使用where子句筛选查询结果”。
(2)在该网站上添加一个Web页面,命名为Default.aspx。在该Web页面上添加一个Button控件和一个Label控件,并为Button控件添加单击事件。Default.aspx页面的代码如下:
示例13-3:使用where子句筛选查询结果
源码路径:光盘\源文件\Chapter13\示例13-3\使用where子句筛选查询结果\Default.aspx


在Default.aspx的代码后置文件中编写Button控件的单击事件,以实现使用where子句筛选数据的功能。Button控件的单击事件使用两条where子句,对查询结果进行筛选,将大于0,小于10的数据显示在Label控件上。Default.aspx文件的后置代码具体如下:
示例13-3:使用where子句筛选查询结果
源码路径:光盘\源文件\Chapter13\示例13-3\使用where子句筛选查询结果\Default.aspx.cs

运行结果如图13.9所示。

图13.9 运行结果图
在该示例中,使用两条where子句对查询结果进行筛选,从查询结果中将大于0且小于10的数据选择出来,输出到Label控件上。
注意:where子句可以筛选查询返回的数据。
13.2.3 使用orderby子句排序查询结果
知识点讲解:光盘\视频讲解\第13章\使用orderby子句排序查询结果.wmv
orderby子句用于对查询结果进行排序,它可以按照一种或多种标准查询结果排序。按照单个标准排序orderby子句的通用形式如图13.10所示。

图13.10 orderby子句通用形式
在orderby子句中,sort-on用来指定排序的标准,可以是数据源中的任意元素;how用来指定排序方向是升序还是降序排序,其值必须是ascending(升序)或者descending(降序)。
注意:默认的排序方式是升序。
【示例13-4】下面演示的是使用orderby子句对查询结果进行排序的方法。
(1)创建一个网站,命名为“使用orderby子句排序查询结果”。
(2)在该网站上添加一个Web页面,命名为Default.aspx。在该Web页面上添加一个Button控件和一个Label控件,并为Button控件添加单击事件。Default.aspx页面的代码如下:
示例13-4:使用orderby子句排序查询结果
源码路径:光盘\源文件\Chapter13\示例13-4\使用orderby子句排序查询结果\Default.aspx

在Default.aspx的代码后置文件中编写Button控件的单击事件,实现使用orderby子句对查询结果进行排序的功能。Button控件的单击事件使用orderby子句将查询结果进行排序,即将从数据源查询到的数据按照从小到大的顺序显示在Label控件上。Default.aspx文件的后置代码具体如下:
示例13-4:使用orderby子句排序查询结果
源码路径:光盘\源文件\Chapter13\示例13-4\使用orderby子句排序查询结果\Default.aspx.cs


运行结果如图13.11所示。

图13.11 运行结果图
在该示例中,使用orderby子句将从数据源查询到的结果进行排序后,输出到Label控件上。默认情况下,排序方向是升序。
13.2.4 select子句
知识点讲解:光盘\视频讲解\第13章\select子句.wmv
select子句用来指定查询要获得何种类型的元素。该子句的通用形式如图13.12所示。

图13.12 select子句通用形式
【示例13-5】下面演示的是select子句的使用方法。
(1)创建一个网站,命名为“select子句的使用方法”。
(2)在该网站上添加一个Web页面,命名为Default.aspx。在该Web页面上添加一个Button控件和一个Label控件,并为Button控件添加单击事件。Default.aspx页面的代码如下:
示例13-5:select子句的使用方法
源码路径:光盘\源文件\Chapter13\示例13-5\select子句的使用方法\Default.aspx

在Default.aspx的代码后置文件中编写Button控件的单击事件,实现select子句的查询功能。Button控件的单击事件使用select子句来获得查询数据的绝对值,并将该绝对值显示在Label控件上。Default.aspx文件的后置代码具体如下:
示例13-5:select子句的使用方法
源码路径:光盘\源文件\Chapter13\示例13-5\select子句的使用方法\Default.aspx.cs


运行结果如图13.13所示。

图13.13 运行结果图
在该示例中,使用select子句获取了从数据源中查询到的满足条件的数据的绝对值。获取方式如下:将满足where子句的结果传递给Math.Abs()方法,该方法用于获取实参的绝对值。最后,再将这些绝对值输出到Label控件上。
注意:select子句可以指定查询获得何种类型的元素。
13.2.5 使用嵌套的from子句
知识点讲解:光盘\视频讲解\第13章\使用嵌套的from子句.wmv
查询可以包含嵌套的from子句。当查询需要从两个不同的数据源获得数据时,就会使用嵌套的from子句。嵌套from子句的通用形式如图13.14所示。

图13.14 嵌套的from子句通用形式
在嵌套from子句的通用形式中,A和B表示两个不同的数据源。
【示例13-6】下面演示嵌套from子句的使用方法。
(1)创建一个网站,命名为“嵌套from子句的使用方法”。
(2)在该网站上添加一个Web页面,命名为Default.aspx。在该Web页面上添加一个Button控件和一个Label控件,并为Button控件添加单击事件。Default.aspx页面的代码如下:
示例13-6:嵌套from子句的使用方法
源码路径:光盘\源文件\Chapter13\示例13-6\嵌套from子句的使用方法\Default.aspx

在Default.aspx的代码后置文件中编写Button控件的单击事件,以实现使用嵌套的from子句查询数据的功能。Button控件的单击事件使用嵌套的from子句获得查询的数据的和值,并将结果的绝对值显示在Label控件上。Default.aspx文件的后置代码具体如下:
示例13-6:嵌套from子句的使用方法
源码路径:光盘\源文件\Chapter13\示例13-6\嵌套from子句的使用方法\Default.aspx.cs


运行结果如图13.15所示。

图13.15 运行结果图
在该示例中,Button控件的单击事件中的查询语句使用了嵌套的from子句。有两个数据源a和b,查询返回的结果是从a和b数据源查找到的数值的和,即a和b数据源中的数据逐一进行相加,结果为九个数据0,5,-4,-5,0,-9,4,9,0。
注意:当查询需要从两个不同的数据源获得数据时,需要使用嵌套的from子句。
13.2.6 使用group子句分组结果
知识点讲解:光盘\视频讲解\第13章\使用group子句分组结果.wmv
group子句是可以结束查询的两种子句(即select或group)之一,它允许创建按照键分组的结果。使用从组中获得的序列,可以方便地访问与特定键相关的所有数据。group子句的通用形式如图13.16所示。

图13.16 group子句的通用形式
注意:在group子句中,返回分组为多个序列的数据,其中每个序列具有由key指定的相同键。
【示例13-7】下面演示使用group子句分组结果的方法。
(1)创建一个网站,命名为“使用group子句分组结果”。
(2)在该网站上添加一个Web页面,命名为Default.aspx。在该Web页面上添加一个Button控件和一个Label控件,并为Button控件添加单击事件。Default.aspx页面的代码如下:
示例13-7:使用group子句分组结果
源码路径:光盘\源文件\Chapter13\示例13-7\使用group子句分组结果\Default.aspx

在Default.aspx的代码后置文件中编写Button控件的单击事件,以实现使用group子句将查询结果分组的功能。Button控件的单击事件使用group子句将查询的数据进行分组,并将结果显示在Label控件上。Default.aspx文件的后置代码具体如下:
示例13-7:使用group子句分组结果
源码路径:光盘\源文件\Chapter13\示例13-7\使用group子句分组结果\Default.aspx.cs


运行结果如图13.17所示。

图13.17 运行结果图
在该示例中,从a数组中查询数据,再使用group子句将查询结果分组。分组依据为i>0,即将查询结果按照是否大于0分为两组,大于0的一组为“1,5,8,47,9”,小于0的一组为“-4,-10,-23,-2”。
13.2.7 使用into子句创建延续
知识点讲解:光盘\视频讲解\第13章\使用into子句创建延续.wmv
在使用select或group子句时,有时我们会希望生成临时结果,随后的查询部分将使用该临时结果产生最终结果。该操作称为查询延续,通过将into子句与select或group子句结合使用可以实现该操作。into子句的通用形式如图13.18所示。

图13.18 into子句通用形式
注意:查询延续体现的概念是:构建新的查询以查询前面的查询操作的结果。
【示例13-8】下面演示的是使用into子句创建延续的方法。
(1)创建一个网站,命名为“使用into子句创建延续”。
(2)在该网站上添加一个Web页面,命名为Default.aspx。在该Web页面上添加一个Button控件和一个Label控件,并为Button控件添加单击事件。Default.aspx页面的代码如下:
示例13-8:使用into子句创建延续
源码路径:光盘\源文件\Chapter13\示例13-8\使用into子句创建延续\Default.aspx

在Default.aspx的代码后置文件中编写Button控件的单击事件,以实现使用into子句创建延续功能。Button控件的单击事件使用into子句创建延续,并将结果显示在Label控件上。Default.aspx文件的后置代码具体如下:
示例13-8:使用into子句创建延续
源码路径:光盘\源文件\Chapter13\示例13-8\使用into子句创建延续\Default.aspx.cs

运行结果如图13.19所示。

图13.19 运行结果图
在该示例中,首先从数组中查询数据,保存为i,然后使用group子句将结果分组,将into子句与group子句结合,实现了查询延续。将数组中的数据按照字符串的开头字母进行分组显示,分为三组:开头为b的单词、开头为r的单词和开头为g的单词。
13.2.8 在查询中使用let子句创建变量
知识点讲解:光盘\视频讲解\第13章\在查询中使用let子句创建变量.wmv
let子句可以创建一个新的范围变量,并用用户提供的表达式的结果初始化该变量。let子句的通用形式如图13.20所示。

图13.20 let子句通用形式
【示例13-9】下面演示的是在查询中使用let子句创建变量的方法。
(1)创建一个网站,命名为“在查询中使用let子句创建变量”。
(2)在该网站上添加一个Web页面,命名为Default.aspx。在该Web页面上添加一个Button控件和一个Label控件,并为Button控件添加单击事件。Default.aspx页面的代码如下:
示例13-9:在查询中使用let子句创建变量
源码路径:光盘\源文件\Chapter13\示例13-9\在查询中使用let子句创建变量\Default.aspx

在Default.aspx的代码后置文件中编写Button控件的单击事件,实现在查询中使用let子句创建变量的功能。Button控件的单击事件中在查询中使用let子句创建变量g,并将结果显示在Label控件上。Default.aspx文件的后置代码具体如下:
示例13-9:在查询中使用let子句创建变量
源码路径:光盘\源文件\Chapter13\示例13-9\在查询中使用let子句创建变量\Default.aspx.cs

运行结果如图13.21所示。

图13.21 运行结果图
在该示例中,首先从a数组中查询数据,然后使用let子句创建一个新的变量g,并将g变量作为数据源,返回查询结果。i.ToCharArray()方法用于将i中的字符复制到Unicode字符数组中。因此,查询结果是将a数组中的字符串转换成字符显示出来。
注意:可以在查询中使用let子句创建中间变量。
13.2.9 使用join子句连接两个序列
知识点讲解:光盘\视频讲解\第13章\使用join子句连接两个序列.wmv
使用join子句可以将来自不同源序列并且在对象模型中没有直接关系的元素进行关联,组要求是每个源中的元素都需要共享一个可以进行比较以判断是否相等的值。join子句的通用形式如图13.22所示。

图13.22 join子句通用形式
【示例13-10】下面演示的是使用join子句连接两个序列的方法。
(1)创建一个网站,命名为“使用join子句连接两个序列”。
(2)在该网站上添加一个Web页面,命名为Default.aspx。在该Web页面上添加一个Button控件和一个Label控件,并为Button控件添加单击事件。Default.aspx页面的代码如下:
示例13-10:使用join子句连接两个序列
源码路径:光盘\源文件\Chapter13\示例13-10\使用join子句连接两个序列\Default.aspx


在Default.aspx的代码后置文件中编写Button控件的单击事件,实现使用join子句连接两个序列的功能。Button控件的单击事件使用join子句将两个数据源连接起来,将结果的绝对值显示在Label控件上。Default.aspx文件的后置代码具体如下:
示例13-10:使用join子句连接两个序列
源码路径:光盘\源文件\Chapter13\示例13-10\使用join子句连接两个序列\Default.aspx.cs


运行结果如图13.23所示。

图13.23 运行结果图
在该示例中,使用join子句将两个数据源--a数组和b数组连接起来,连接条件是a[4]与b[4]的值相等。该LINQ语句的返回值为两个数据源字符串连接后的值。
注意:使用join子句,可以将来自不同源序列并且在对象模型中没有直接关系的元素相关联。
13.3 LINQ表达式基础
LINQ是一组技术的名称,这些技术建立在将查询功能直接集成到C#语言的基础上。13.2节中简单讲述了查询表达式中的上下文关键字。本节来学习LINQ表达式基础,主要讲解的有扩展方法、Lambda表达式和表达式树。
13.3.1 扩展方法
知识点讲解:光盘\视频讲解\第13章\扩展方法.wmv
扩展方法提供了为类添加功能而不必使用普通继承机制的方式。LINQ查询多次使用group、orderby和where子句进行查询操作,它的子句都会被数据源的扩展方法代替。扩展方法的通用形式如图13.24所示。

图13.24 扩展方法的通用形式
扩展方法的特点具体说明如下:
- 该方法是静态方法,必须包含在静态非泛型类中。
- 必须通过this关键字修饰第一个参数。扩展方法第一个参数的类型确定了可以对其调用该扩展方法的对象的类型。
- 扩展方法由第一个类型参数指定的实例对象来调用。
【示例13-11】下面演示的是扩展方法的使用方法。
(1)创建一个网站,命名为“扩展方法的使用方法”。
(2)在该网站上添加一个Web页面,命名为Default.aspx。在该Web页面上添加一个Button控件和一个Label控件,并为Button控件添加单击事件。Default.aspx页面的代码如下:
示例13-11:扩展方法的使用方法
源码路径:光盘\源文件\Chapter13\示例13-11\扩展方法的使用方法\Default.aspx

在Default.aspx的代码后置文件中编写Button控件的单击事件。该单击事件编写了一个扩展方法f(),该方法有两个参数,返回两个参数的商的绝对值,将结果显示在Label控件上。Default.aspx文件的后置代码具体如下:
示例13-11:扩展方法的使用方法
源码路径:光盘\源文件\Chapter13\示例13-11\扩展方法的使用方法\Default.aspx.cs

运行结果如图13.25所示。

图13.25 运行结果图
在该示例中,将扩展方法写在命名空间A的类A中。该扩展方法名为f,有两个参数,方法返回值为两个参数的商的绝对值。调用该扩展方法时,语句“b = a.f(5);”将a的值(即主调对象)传递给扩展方法的第一个参数i,5传递给第二个参数j,因此返回值为2。
注意:扩展方法是静态方法,必须放在静态非泛型类中。
13.3.2 Lambda表达式
知识点讲解:光盘\视频讲解\第13章\Lambda表达式.wmv
Lambda表达式是LINQ查询表达式的重要组成部分,是一种典型的匿名函数。Lambda表达式可以包含表达式和语句,并且适用于创建委托和事件。该表达式由输入参数、Lambda运算符和表达式(或语句块)构成。Lambda表达式的通用形式如图13.26所示。

图13.26 Lambda表达式的通用形式
在Lambda表达式中,左半部分表示指定的输入参数,右半部分是Lambda表达式主体,中间的是Lambda运算符,该运算符被描述为“转到”或goes to。当为Lambda表达式传入多个参数时,必须使用括号将所有参数括起来。
注意:在is或as运算符的左侧不允许使用Lambda。
【示例13-12】下面演示的是Lambda表达式的使用方法。
(1)创建一个网站,命名为“Lambda表达式的使用方法”。
(2)在该网站上添加一个Web页面,命名为Default.aspx。在该Web页面上添加一个Button控件和一个Label控件,并为Button控件添加单击事件。Default.aspx页面的代码如下:
示例13-12:Lambda表达式的使用方法
源码路径:光盘\源文件\Chapter13\示例13-12\Lambda表达式的使用方法\Default.aspx


在Default.aspx的代码后置文件中编写Button控件的单击事件。首先声明一个委托del(委托的声明使用delegate关键字),该委托带了一个整型的参数;在Button控件的单击事件中创建一个委托的实例myDelegate,并将Lambda表达式作为方法向委托注册;然后调用委托,将会调用Lambda表达式;最后将结果显示在Label控件上。Default.aspx文件的后置代码具体如下:
示例13-12:Lambda表达式的使用方法
源码路径:光盘\源文件\Chapter13\示例13-12\Lambda表达式的使用方法\Default.aspx.cs

运行结果如图13.27所示。

图13.27 运行结果图
在该示例中,声明一个委托,将Lambda表达式作为方法向委托注册方法,然后在Button控件的单击事件中调用委托,并将结果显示在Label控件上。
13.3.3 表达式树
知识点讲解:光盘\视频讲解\第13章\表达式树.wmv
表达式树是另一个与LINQ相关的功能,它将Lambda表达式表示为数据,因此其自身不可以执行。然而通过调用Expression类定义的Compile()方法,可以获得表达式树的可执行形式,该方法返回可以赋给委托然后执行的引用。表达式树只可以表示表达式Lambda,而不可以用于表示语句Lambda。表达式树的通用形式如图13.28所示。

图13.28 表达式树的通用形式
注意:编译器仅可以通过表达式Lambda(或单行Lambda)生成表达式树。
【示例13-13】下面演示表达式树的使用方法。
(1)创建一个网站,命名为“表达式树的使用方法”。
(2)在该网站上添加一个Web页面,命名为Default.aspx。在该Web页面上添加一个Button控件和一个Label控件,并为Button控件添加单击事件。Default.aspx页面的代码如下:
示例13-13:表达式树的使用方法
源码路径:光盘\源文件\Chapter13\示例13-13\表达式树的使用方法\Default.aspx


在Default.aspx的代码后置文件中编写Button控件的单击事件。在Button控件的单击事件中,首先创建一个表达式树,然后将表达式树转换成可执行代码,最后调用xyz,将结果显示在Label控件上。Default.aspx文件的后置代码具体如下:
示例13-13:表达式树的使用方法
源码路径:光盘\源文件\Chapter13\示例13-13\表达式树的使用方法\Default.aspx.cs


运行结果如图13.29所示。

图13.29 运行结果图
在该示例中,创建表达式树将Lambda表达式表示为数据,然后将表达式树转换为可执行代码,最后调用xyz,将结果显示在Label控件上。
注意:“(n, d) => (d != 0) ? (n % d) == 0 : false;”语句是Lambda表达式,该表达式中使用了条件运算符,即当(d != 0)为真,则表达式结果为(n % d) == 0的值;反之,表达式结果为false。
13.4 LINQ To DataSet操作内存表
.NET 3.5之后,LINQ To DataSet技术赋予了DataSet对象强大的查询能力。使用LINQ To DataSet,可以方便、快速地查询DataSet中的对象。本节我们来学习使用LINQ To DataSet操作内存表的方法。
13.4.1 LINQ To DataSet简介
知识点讲解:光盘\视频讲解\第13章\LINQ To DataSet简介.wmv
LINQ To DataSet技术主要用于在DataSet的DataTable对象或在DataView对象中构建快捷、有效的查询。该查询具有四个特点,具体描述如下:
- 不能查询空的DataSet对象,即该对象中必须被填充数据。
- DataTable与DataView对象都没有实现IEnumerable<T>接口,所以在使用它们实现查询时,必须调用其AsEnumerable()方法,将它们转化为LINQ查询表达式能接受的数据源。AsEnumerable()方法位于System.Data命名空间的DataTableExtension类中。
- LINQ To DataSet技术最终查询是在DataRow对象或DataRowView对象上实现的。查询程序数据的方法与查询一般数据对象基本一致。
- 使用LINQ To DataSet技术时,需要导入System.Linq、System.Data两个DLL。它们包含针对DataSet、DataTable和DataView对象的扩展方法,而该技术的实现主要基于这些扩展方法。
在LINQ To DataSet查询中,首先将DataTable转换为实现了IEnumerable<T>接口的数据源,然后构建一个简单的LINQ查询表达式,该表达式中的查询建立在DataRow对象集合上,调用此对象的Field<T>()方法来获取当前行中特定列的值,该值可以完成数据查询、筛选操作。
注意:执行LINQ To DataSet查询时,不提倡使用DataRow对象的索引器直接获取特定列值。这是因为某些列的值为null时会导致查询错误。推荐使用DataRow对象的Field<T>()方法获取当前行中的特定列的值,以完成数据查询。该方法位于System.Data命名空间的DataRowExtension类中。
13.4.2 连接查询
知识点讲解:光盘\视频讲解\第13章\连接查询.wmv
LINQ To DataSet技术的实现主要基于DataTable、DataView、DataRow或DataRowView对象的扩展方法。当使用该技术查询多表数据时,需要将这些数据导入同一个DataSet中。本小节我们来学习连接查询。
【示例13-14】下面演示连接查询的使用方法。
(1)创建一个网站,命名为“连接查询的使用方法”。
(2)在该网站上添加一个Web页面,命名为Default.aspx。在该Web页面上添加一个Label控件、两个Button控件和一个GridView控件。Default.aspx页面的代码如下:
示例13-14:连接查询的使用方法
源码路径:光盘\源文件\Chapter13\示例13-14\连接查询的使用方法\Default.aspx


在Default.aspx的代码后置文件中编写Button控件的单击事件,以实现连接查询的功能,并将查询结果显示在GridView控件上。Default.aspx文件的后置代码具体如下:
示例13-14:连接查询的使用方法
源码路径:光盘\源文件\Chapter13\示例13-14\连接查询的使用方法\Default.aspx.cs


Button1控件实现的是单表查询。单击Button1时,将调用SingTableQuery()方法,查询DataSet中Student表的内容,代码如下:
示例13-14:连接查询的使用方法
源码路径:光盘\源文件\Chapter13\示例13-14\连接查询的使用方法\Default.aspx

Button2控件实现的是多表查询。单击Button2时,将调用MorethanOneTableQuery()方法,查询DataSet中Student表和Class表的内容,代码如下:
示例13-14:连接查询的使用方法
源码路径:光盘\源文件\Chapter13\示例13-14\连接查询的使用方法\Default.aspx

运行结果如图13.30和图13.31所示。
![]() |
![]() |
图13.30 运行结果图(1) | 图13.31 运行结果图(2) |
在该示例中,单击“单表查询”按钮时,执行SingTableQuery()方法,查询Student表中的内容;单击“多表查询”按钮时,执行MoreThanOneTableQuery()方法,查询Student表和Class表中的内容。在FillDataSet()方法中,一次性向DataSet对象中插入了两张表的数据,通过调用DataSet对象的AsEnumerable()方法,将数据表转换为数据源。在MoreThanOneTableQuery()方法中,使用Join操作符查询两个表的数据。最后,将查询结果通过GridView控件显示出来。
注意:LINQ To DataSet技术的实现主要基于DataTable、DataView、DataRow或DataRowView对象的扩展方法。
13.5 LINQ To SQL
LINQ的重要的特性是使用LINQ的查询表达式可以查询数据库,LINQ将会转换其查询语法为相应的SQL语言。在实际开发中,LINQ To SQL是使用最多的技术。本节我们来学习LINQ To SQL相关的知识。
13.5.1 数据实体类
知识点讲解:光盘\视频讲解\第13章\数据实体类.wmv
LINQ To SQL技术可以帮助开发人员迅速构建数据操作层,并完成数据到实体对象的映射。在构建数据操作层时,需要首先构建数据实体类,并建立它与数据表或视图间的映射关系。本小节我们来了解数据实体类。
当使用LINQ To SQL从数据库中读出记录时,这些数据库记录将被转换为一组内存对象。LINQ To SQL对象模型中最基本的元素及其与关系数据模型中的元素关系如表13.3所示。
表13.3 LINQ To SQL实体类与数据库关系

数据库中的表被表示为一个类。构建数据实体类与数据表的映射关系需要用到一些特性,具体如表13.4所示。
表13.4 映射特性
特性 | 说明 |
TableAttribute | 标识当前实体类对应的数据表。该特性只能修饰实体类,具有Name属性,用于显示标识实体类对应的数据表名称。若Name属性缺省,则当前实体类对应的数据表与实体类同名 |
ColumnAttribute | 标识当前实体类属性对应的数据列名称及数据列约束。该特性只能修饰属性,具有Name属性,用于标识当前属性对应的数据列名称。若Name属性缺省,则当前属性对应的数据列名称与属性同名。具有CanBeNull属性,用于标识数据列是否允许为空 |
使用TableAttribute和ColumnAttribute特性时,需要注意一些方面,具体说明如下:
- 使用TableAttribute特性标识实体类对应的数据表时,需要注意数据表名称中是否具有空格。如果出现了特殊的数据表名称,需要使用“[”、“]”将其括起来,再为Name属性赋值。
- 为ColumnAttribute特性的Name属性赋值时,需要注意数据表名称。默认情况下,CanBeNull属性值为true,即实体属性对应的数据列允许为空。
一个数据库实体类Student的代码如下所示:

注意:使用表13.4中的特性时,需要导入System.Data.Linq.Mapping命名空间。该命名空间中还内置了很多其他的特性,用于映射表间关系、视图等数据库对象。若无法找到命名空间System.Data.Linq,可在“解决方案资源管理器”面板的网站上单击右键,选择“属性页”命令,在“属性页”对话框中单击“引用”链接,然后单击“添加”按钮,在打开的“引用管理器”中找到System.Data.Linq,单击“确定”按钮,如图13.32所示,即可引用System.Data.Linq程序集。

图13.32 引用System.Data.Linq程序集
13.5.2 DataContext类
知识点讲解:光盘\视频讲解\第13章\DataContext类.wmv
数据实体类是数据载体,不具有获取数据、操作数据的能力。.NET Framework内置了DataContext类来封装各种数据操作。DataContext类是LINQ To SQL的入口,是连接数据库、从中检索对象以及将更改提交回数据库的主要渠道。DataContext对象是LINQ To SQL的核心对象。本小节来学习DataContext类的相关知识。DataContext对象的功能如图13.33所示。

图13.33 DataContext对象的功能
注意:DataContext类属于System.Data.Linq命名空间。
【示例13-15】下面演示DataContext类的使用方法。
(1)创建一个网站,命名为“DataContext类的使用方法”。
(2)在该网站上添加一个Web页面,命名为Default.aspx。在该Web页面上添加一个Button控件、一个Label控件和一个GridView控件。Default.aspx页面的代码如下:
示例13-15:DataContext类的使用方法
源码路径:光盘\源文件\Chapter13\示例13-15\DataContext类的使用方法\Default.aspx

在本例中,需要访问Express数据库中的Student表。因此,首先需要为Student表编写实体类。该实体类代码如下:
示例13-15:DataContext类的使用方法
源码路径:光盘\源文件\Chapter13\示例13-15\DataContext类的使用方法\Default.aspx.cs


在Default.aspx的代码后置文件中编写Button控件的单击事件。在Button控件的单击事件中调用QueryTable()方法,该方法使用DataContext对象的GetTable<T>()方法,访问指定的表Student,并将LINQ查询语法转换后的SQL语句显示在Label控件上,最后将查询结果作为数据源赋给GridView控件,显示出来。Default.aspx文件的后置代码具体如下:
示例13-15:DataContext类的使用方法
源码路径:光盘\源文件\Chapter13\示例13-15\DataContext类的使用方法\Default.aspx.cs


运行结果如图13.34所示。

图13.34 运行结果图
在该示例中,先将数据库Express中的表Student转换成实体类,再使用DataContext对象的GetTable<T>()方法来访问Student表,并将LINQ查询语法转换后的SQL语句显示在Label控件上,最后将查询结果作为数据源赋给GridView控件,显示出来。
13.5.3 应用LINQ To SQL
知识点讲解:光盘\视频讲解\第13章\应用LINQ To SQL.wmv
在编写ASP.NET应用程序时,最好将数据访问代码放在一个单独的类中,这样做的好处是便于修改、调试和维护。LINQ To SQL同样需要将数据访问代码单独放在一个类中。本小节来学习在ASP.NET中应用LINQ To SQL的方法。
【示例13-16】下面演示在ASP.NET中应用LINQ To SQL查询数据的方法。
(1)创建一个网站,命名为“在ASP.NET中应用LINQToSQL”。
(2)在该网站的Web.config文件中配置数据库连接字符串,该连接字符串添加在<configuration>下的<appSetting>中。具体配置代码如下:
示例13-16:在ASP.NET中应用LINQToSQL
源码路径:光盘\源文件\Chapter13\示例13-16\在ASP.NET中应用LINQToSQL\Web.config

(3)在该网站上添加一个类Program,在Program类中创建一个访问Express数据库中Student表的方法。本例需要访问Express数据库中的Student表,因此,首先需要为Student表编写实体类。该实体类代码如下:
示例13-16:在ASP.NET中应用LINQToSQL
源码路径:光盘\源文件\Chapter13\示例13-16\在ASP.NET中应用LINQToSQL\App_Code\Program.cs


Program类的具体代码如下:
示例13-16:在ASP.NET中应用LINQToSQL
源码路径:光盘\源文件\Chapter13\示例13-16\在ASP.NET中应用LINQToSQL\App_Code\Program.cs

(4)在该网站上添加一个Web页面,命名为Default.aspx。在该Web页面上添加一个Button控件、一个Label控件和一个GridView控件。Default.aspx页面的代码如下:
示例13-16:在ASP.NET中应用LINQToSQL
源码路径:光盘\源文件\Chapter13\示例13-16\在ASP.NET中应用LINQToSQL\Default.aspx


在Default.aspx的代码后置文件中编写Button控件的单击事件。Button控件的单击事件创建了Program类的实例p,使用p调用方法GetStudent(),将返回值作为GridView控件的数据源,最终将结果显示在GridView控件上。Default.aspx文件的后置代码具体如下:
示例13-16:在ASP.NET中应用LINQToSQL
源码路径:光盘\源文件\Chapter13\示例13-16\在ASP.NET中应用LINQToSQL\Default.aspx.cs


运行结果如图13.35所示。

图13.35 运行结果图
在该示例中,首先创建了一个访问Student表的类Program。在Button控件的单击事件中创建Program类的实例p,使用p调用方法GetStudent(),将返回值作为GridView控件的数据源,最终将结果显示在GridView控件上。
注意:将数据访问代码放在一个单独的类中,有助于代码的修改、调试和维护。
13.5.4 自动生成数据实体类
知识点讲解:光盘\视频讲解\第13章\自动生成数据实体类.wmv
使用Visual Studio 2012的设计器添加一个LINQ To SQL类之后,可以手动拖动数据表到设计器中。如果需要,也可以在设计器中手动设计实体类。本小节来学习在Visual Studio 2012中如何自动生成数据实体类和存储过程方法。
添加LINQ To SQL类后,Visual Studio 2012会自动添加一个DBML文件,并创建两个附加的资源文件。3个文件的后缀名及其含义如下:
- .dbml:定义数据库的架构。
- .dbml.layout:定义每个表在设计视图中的布局。
- .dbml.designer.cs:包含自动生成的类。
在设计器中设计完表结构后,将该文件保存。Visual Studio 2012将会自动生成两种类型的类,一种是为数据库中的表添加类,另一种是一个派生自DataContext的强类型类(该类以DBML文件名后跟DataContext作为名称,与强类型DataSet类似)。该类提供了辅助方法允许快速获取表的内容。
注意:添加LINQ To SQL类后,Visual Studio 2012会自动生成3个文件。
【示例13-17】下面演示如何自动生成数据实体类。
(1)创建一个网站,命名为“自动生成数据实体类”。
(2)在该网站上添加一个LINQ To SQL类,命名为DataClasses.dbml。该文件界面如图13.36所示。

图13.36 DBML文件设计视图
(3)连接数据库。打开“服务器资源管理器”面板,在“数据连接”项上单击右键,选择“添加连接”命令。在打开的“添加连接”对话框中将服务器名、登录服务器身份验证方式和数据库名称依次填好,如图13.37所示。然后单击“测试连接”按钮,检查数据库是否连接成功。

图13.37 连接数据库
(4)数据库连接成功后,从“服务器资源管理器”面板中将表Student拖动到左侧设计器中,如图13.38所示。

图13.38 拖动表Student到设计器中
(5)至此,数据实体类创建成功。打开DataClasses.designer.cs文件,即可看到生成的两个类。生成的派生自DataContext的强类型类DataClassesDataContext的代码如下:
示例13-17:自动生成数据实体类
源码路径:光盘\源文件\Chapter13\示例13-17\自动生成数据实体类\App_Code\DataClasses.designer.cs

为Student表生成的类Student的部分代码如下:
示例13-17:自动生成数据实体类
源码路径:光盘\源文件\Chapter13\示例13-17\自动生成数据实体类\App_Code\DataClasses.designer.cs


在该示例中,使用Visual Studio 2012提供的工具自动生成数据实体类。在实际项目开发中,数据库架构经常会更改,就要重建实体类。因此,建议将在数据实体类中添加的代码写在一个单独的文件中,以避免被覆盖。
13.5.5 数据操作
知识点讲解:光盘\视频讲解\第13章\数据操作.wmv
在LINQ To SQL中可以对数据进行操作。使用DataContext对象,可以使用标准方法添加、修改和删除数据。本小节将给大家讲解在LINQ To SQL中操作数据的方法。
【示例13-18】下面演示的是LINQ To SQL中数据操作的方法。
(1)创建一个网站,命名为“演示使用LINQToSQL操作数据”。
(2)在该网站上添加一个Web页面,命名为Default.aspx。在该Web页面上添加四个Button控件和一个GridView控件。Default.aspx页面的代码如下:
示例13-18:使用LINQToSQL操作数据
源码路径:光盘\源文件\Chapter13\示例13-18\演示使用LINQToSQL操作数据\Default.aspx

在Default.aspx的代码后置文件中编写四个Button控件的单击事件。Button1控件实现查询数据的功能;Button2控件实现插入数据的功能;Button3控件实现删除数据的功能;Button4控件实现更新数据的功能。首先编写一个获取表Student数据的方法GetQuery(),代码如下:
示例13-18:使用LINQToSQL操作数据
源码路径:光盘\源文件\Chapter13\示例13-18\演示使用LINQToSQL操作数据\Default.aspx.cs

第1个Button控件实现的是查询数据的功能。在该按钮的单击事件中,调用GetQuery()方法即可实现查询数据功能,代码如下:
示例13-18:使用LINQToSQL操作数据
源码路径:光盘\源文件\Chapter13\示例13-18\演示使用LINQToSQL操作数据\Default.aspx.cs

第2个Button控件实现的是插入数据的功能。在该按钮事件中创建数据实体类的实例,并将插入数据信息输入;使用InsertOnSubmit()方法将记录添加到表中,再调用SubmitChanges()方法向数据库提交更改;最后调用GetQuery()方法,将返回值作为GridView控件的数据源,将数据显示出来。Button2的单击事件代码如下:
示例13-18:使用LINQToSQL操作数据
源码路径:光盘\源文件\Chapter13\示例13-18\演示使用LINQToSQL操作数据\Default.aspx.cs

第3个Button控件实现的是删除数据的功能。在该按钮事件中创建StudentDataContext类的实例,从数据库的表中查询到需要删除的记录;使用DeleteAllOnSubmit()方法将符合查询条件的记录从表集合中删除,再调用SubmitChanges()方法向数据库提交更改;最后调用GetQuery()方法,将返回值作为GridView控件的数据源,将数据显示出来。Button3的单击事件代码如下:
示例13-18:使用LINQToSQL操作数据
源码路径:光盘\源文件\Chapter13\示例13-18\演示使用LINQToSQL操作数据\Default.aspx.cs

第4个Button控件实现的是更新数据的功能。在该按钮事件中创建StudentDataContext类的实例,从数据库的表中查询到需要修改的记录;使用foreach语句将符合查询条件的记录在表集合中修改,再调用SubmitChanges()方法向数据库提交更改;最后调用GetQuery()方法将返回值作为GridView控件的数据源,将数据显示出来。Button4的单击事件代码如下:
示例13-18:使用LINQToSQL操作数据
源码路径:光盘\源文件\Chapter13\示例13-18\演示使用LINQToSQL操作数据\Default.aspx.cs

运行结果如图13.39~图13.42所示。
![]() |
![]() |
图13.39 运行结果图(查询数据) | 图13.40 运行结果图(插入数据) |
![]() |
![]() |
图13.41 运行结果图(删除数据) | 图13.42 运行结果图(更新数据) |
在该示例中,添加了4个按钮,并为其编写单击事件,分别实现了查询数据、添加数据、删除数据和更新数据的功能。执行完该示例,打开SQL Server 2012中的Express数据库的Student表,如图13.43所示。

图13.43 查询表Student的数据
注意:从图13.43中可以看到,数据库中的数据被修改了,也就是说,通过程序更新了数据库中的数据。
13.6 小结
本章主要介绍了ASP.NET 4.5中的语言集成查询LINQ,包括LINQ基础知识、查询表达式中的上下文关键字、LINQ表达式基础、LINQ To DataSet操作内存表和LINQ To SQL。重点是对LINQ查询表达式和LINQ To SQL的掌握,难点是对LINQ To DataSet的掌握。有兴趣的读者可以尝试使用LINQ查询,为自己的网站添加更多的功能。
13.7 本章习题
习题13-1 在Visual Studio 2012中新建一个网站,命名为chapter13_1。在该网站上添加一个Web页面,命名为Default.aspx。在该页面上添加一个Button控件和一个Label控件。在Button控件的单击事件中使用orderby子句将查询结果排序,运行结果如图13.44所示。

图13.44 运行结果图
【分析】本题目主要考查的是对使用orderby子句排序查询结果的掌握情况。
【关键代码】Default.aspx.cs文件代码如下:
public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } private string[] s = { "apple","fly","area","color"}; protected void Button1_Click(object sender, EventArgs e) { var result = from i in s orderby i select i; Label1.Text = "查询结果如下:<br /><br />"; foreach (string i in result) { Label1.Text += i + "<br />"; } } }
习题13-2 在Visual Studio 2012中新建一个网站,命名为chapter13_2。在该网站上添加一个Web页面,命名为Default.aspx。在该页面上添加一个Button控件和一个Label控件。使用Lambda表达式将两个String类型的变量进行连接。运行结果如图13.45所示。

图13.45 运行结果图
【分析】本题目主要考查的是对Lambda表达式的掌握情况。
【关键代码】Default.aspx.cs文件代码如下:
public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } delegate string MyDelete(string s); protected void Button1_Click(object sender, EventArgs e) { MyDelete del = x => x + x; string i = del("hello"); Label1.Text = "查询结果如下:"; Label1.Text += i.ToString(); } }
习题13-3 在Visual Studio 2012中新建一个网站,命名为chapter13_3。在该网站上添加一个Web页面,命名为Default.aspx。在该页面上添加一个Button控件和一个Label控件。在Default.aspx.cs文件代码中使用表达式树判断因子问题,运行结果如图13.46所示。

图13.46 运行结果图
【分析】本题目主要考查的是对表达式树的掌握情况。
【关键代码】Default.aspx.cs文件代码如下:
public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void Button1_Click(object sender, EventArgs e) { Expression<Func<int, int, bool>> abc = (n, d) => (d != 0) ? (n % d) == 0 : false; Func<int, int, bool> xyz = abc.Compile(); Label1.Text = "查询结果为:<br /><br />"; if (xyz(9, 3)) { Label1.Text += "3是9的因子<br />"; } if (!xyz(9, 4)) { Label1.Text += "4不是9的因子<br />"; } if (!xyz(10, 5)) { Label1.Text += "5不是10的因子"; } } }
习题13-4 在Visual Studio 2012中新建一个网站,命名为chapter13_4。在该网站上添加一个Web页面,命名为Default.aspx。在该页面上添加一个Button控件、一个Label控件和一个GridView控件。在Button控件的单击事件中使用DataContext对象的GetTable<T>()方法,访问指定的表fruit(该表为习题11-2中创建的表)。运行结果如图13.47所示。

图13.47 运行结果图
【分析】本题目主要考查的是对DataContext类的掌握情况。
注意:在使用SQL Server数据库中的表之前,需要先写数据实体类。
【关键代码】Default.aspx.cs文件代码如下:
using System.Data.Linq.Mapping; using System.Data.Linq; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void Button1_Click(object sender, EventArgs e) { string Connection = "Data Source=Localhost;User ID=sa;Password=123456;Database=MySQLData"; DataContext context = new DataContext(Connection); Table<fruit> stu = context.GetTable<fruit>(); var result = from i in stu select i; Label1.Text = "查询结果为:<br /><br />"; GridView1.DataSource = result; GridView1.DataBind(); } }
表fruit的数据实体类代码如下:
[Table(Name = "fruit")] public class fruit { [Column(IsPrimaryKey = true)] public string Name { get; set; } [Column] public string Taste { get; set; } }
习题13-5 在Visual Studio 2012中新建一个网站,命名为chapter13_5。在该网站上添加一个Web页面,命名为Default.aspx。在该页面上添加一个Button控件、一个Label控件和一个GridView控件。运行结果如图13.48所示。

图13.48 运行结果图
【分析】本题目主要考查的是对LINQ To SQL的掌握情况。
【关键代码】Default.aspx.cs文件代码如下:
using System.Data.Linq; using System.Data.Linq.Mapping; using System.Configuration; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void Button1_Click(object sender, EventArgs e) { string s = ConfigurationManager.AppSettings["ConnectionString"].ToString(); DataContext context = new DataContext(s); GridView1.DataSource = context.GetTable <fruit>(); try { GridView1.DataBind(); Label1.Text = "数据库连接成功!<br />"; } catch (Exception ex) { Label1.Text = "数据库连接出错!" + ex.Message; } } }
Web.config文件代码如下:
<configuration> <appSettings> <add key="ConnectionString" value="Data Source=Localhost;User ID=sa;Password=123456; Database= MySQLData"/> </appSettings> <system.web> <compilation debug="true" targetFramework="4.5"> <assemblies> <add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken= B77A5C561 934E089"/> </assemblies> </compilation> <httpRuntime targetFramework="4.5"/> </system.web> </configuration>
表fruit的数据实体类代码如下:
[Table(Name = "fruit")] public class fruit { [Column(IsPrimaryKey = true)] public string Name { get; set; } [Column] public string Taste { get; set; } }
共有条评论 网友评论