1、1 1第10章 面向服务软件和数据库的接口 10.1 面向服务软件中的数据库面向服务软件中的数据库 10.2 面向服务软件中的关系数据库面向服务软件中的关系数据库 10.3 基于基于XML的数据库和查询语言的数据库和查询语言 XQuery 10.4 综合查询语言综合查询语言LINQ 10.5 讨论讨论2 2本章讨论如何通过数据适配器连接传统的数据库与面向服务软件,不通过适配器直接与SOA软件通信的基于XML的数据库的开发,以及用统一的方式访问数据对象、传统数据库和XML数据的综合查询语言(LINQ)等问题。3 3数据管理和数据库是所有分布式系统中的一个完整部分,它通常在一个四层结构中构成完整的
2、一层。在提出SOA和SOC很久之前,数据库就已存在并广泛使用。当前主要的问题是数据库格式如何与SOC的数据格式有效通信,因为当前的数据库格式是列、行、表的集合,而SOC的数据格式大部分是基于XML的树状结构。10.1 面向服务软件中的数据库面向服务软件中的数据库4 4图10.1所示的是SOA系统和组件结合应用的场景。图的上部分是不同类型的SOA的应用,包括Windows应用、Web应用、移动和机器人应用以及诸如用BPEL编写的组合工作流应用。这些SOA应用可以使用Internet上的外部资源库中的远程服务执行应用之外的功能。图的下部分是支持SOA应用的数据管理和数据库。传统的关系数据库,如DB
3、2、OLE、Oracle和SQL数据库,已经支持商务数据数十年,在现在和以后的许多年里,它们仍将在软件系统中扮演重要角色。目前的解决方案是:5 5对每一个数据库,都用一个数据适配器将该数据库的数据格式转换成SOA应用的标准接口,以便SOA应用中的代码独立于实际使用的数据库。每个数据库都有自己的数据适配器,数据适配器输出的是标准的数据集。这个数据集是表的集合,每一个表又由行和列组成。数据库访问包括读写二级存储器,例如磁盘。这种访问的延迟时间即使不是百万倍地增加,也将会是数千倍地下降。因此,访问数据库的有效方法是一次就读或者写一个可能多次使用的大的数据块。数据集为数据库的读写提供了一个缓冲区。6
4、6图10.1 面向服务软件中的数据库7 7标准数据集可以直接发送给应用进行处理。数据管理层在将数据集发送给应用之前,将它转换为文本或XML格式,在处理能力有限的移动和嵌入式应用中更是如此。虽然当前的业务都使用的是关系数据库,但对基于的数据库仍在积极寻求可消除适配器层的更好的解决方案。关系数据库为每一个数据项存储相同格式的信息,因此所有的数据项可以存储在一个由行和列组成的平面表中。XML数据库用树结点表示数据项,因此它能够在不同的数据项中存储不同格式的信息。8 8我们的挑战是开发一种可以有效表达数据查询的查询语言,它便于用户使用并且可以通过硬件高效执行。本体是基于XML的,本体数据库存储能够描述
5、数据性质和属性的元数据。本体可以看做是一种高级数据库和基于知识的系统,并发挥着越来越重要的作用。9 910.2.1 数据库和软件之间的接口数据库和软件之间的接口数据库,例如磁盘文件,可以看做包含对象集合的文档。读写数据库就是将对象从源文档转移到目标文档。因此,通过文档转移可实现数据库适配。10.2 面向服务软件中的关系数据库面向服务软件中的关系数据库10 10OLE(ObjectLinkingandEmbedding)是一个复合文档标准,由微软开发并于1990年首次发布。OLE允许在一个应用内创建对象,然后链接或嵌入到另一个应用中。嵌入对象保持原有格式,并和创建它们的应用相连接。作为一个分布式
6、对象系统和协议,OLE能够访问不同的数据库。OLE内置于Windows和MacOS操作系统中。11 11Apple和IBM公司开发了另一个竞争性的复合文档标准,称为OpenDoc,它是一个开源的、多平台的体系结构,支持基于组件的软件开发。从1993年最初的版本开始,分布式对象模型OLE经历过好几代的演化。在1993年,OLE扩展成“网络OLE”,然后再到组件对象模型COM(ComponentObjectModel),MacOS、Unix和Windows系统都支持它,但其主要还是应用于Windows。在Windows2000中,对COM做了重要扩展并将其更名为COM,后来又演化为DCOM。12
7、12OLE的另一个扩展是ActiveX数据库对象ADO(ActiveXDataObjectModel),它是编程语言和OLEDB的中间层,提供了一种独立于语言访问数据库的方式。ADO允许开发人员编写访问数据的程序而无需知道数据库是如何实现的。DCOM和ADO中的所有技术都集成到VisualStudio.Net,它是一个一体化的、面向对象的、分布式的、面向服务的软件开发环境。ADO.Net取代了ADO,并且在很多方面对其做了改进。表10.1对ADO和ADO.Net的特点进行了对比。13 13表10.1 ADO与ADO.Net的比较 14 14为了在ADO中访问数据库对象,需要实例化一个连接对象,
8、调用这个对象的open方法,并传给它一个连接字符串,然后,通过对象中不同的方法访问对象。该链接一直处于活动状态并且对象被锁定,直到调用close方法关闭它。在ADO.Net中,也使用同样的原理。然而,它实例化一个System.Data.SqlClient名字空间中的SqlConnection对象,意味着该命令作为查询命令提交给数据库。这种查询是基于消息的,并不锁定数据库对象。15 15和ADO相比,ADO.Net支持更通用的数据结构。ADO.Net的数据集(Dataset)类似于一个三维数据集合,它允许你分层访问数据结构,数据结构的切片是一个表、一行或者一列、或者行与列中的一个单独的元素。AD
9、O有一个固定的记录集(RecordSet),它是一个表。ADO使用二进制模式传输数据,而ADO.Net支持文本模式的基于XML的数据传输,在当今的商业界,这是非常重要的。出于安全考虑,几乎没有防火墙允许二进制数据在客户端和服务器端自由传输。16 16数据库和应用或服务连接的关键组件是数据适配器和数据提供者。数据适配器直接和数据库通信,并给数据提供者提供了独立于数据库的接口,数据提供者提供读写数据库的连接和方法。图10.2是一个使用SQL数据库和相关库函数的例子。17 17图10.2 支持FCL类和数据库连接的数据适配器和数据提供者18 18ADO.Net通过.Net框架类库FCL(Framew
10、orkClassLibrary)中的不同类访问不同的数据库。名字空间用于访问特定的数据库,它包括一个连接类、一个命令类和一个适配器类,如图10.3所示。.Net的本地数据库是SQL数据库,.NetFCL管理所有需要的类。对于其他数据库,包括Oracle、OLE、ODBC(Access),需要一个由.NetFCL外部数据服务组成的转换器连接FCL类和数据库。19 19图10.3 ADO.Net支持不同的数据库202010.2.2 ADO.Net中的中的SQL数据库数据库在本节,我们将用SQL数据库演示ADO.Net的使用。SQL数据库的接口在ADO.Net中同样可用,参看图10.2。1.创建数据
11、库项开始时,需要在.Net环境中创建一个SQL数据库。可以依照下面的步骤在现有的项目中可创建一个数据库项:(1)在解决方案资源管理器中单击要添加数据库文件的结点。(2)在项目菜单上,点击添加已存在项。21 21(3)出现添加已存在项对话框,如图10.4所示。Visualstudio2008支持两种类型的数据库:通过ADO.NetSQL接口访问的本地数据库;通过LINQ(LanguageIntegratedQuery)访问的基于服务的数据库。本节论选项,在后面的部分将讨论选项。2222图10.4 给应用添加数据库2323(4)在文件名中,输入想要加入数据库项目中的文件的路径和文件名。输入的文件名
12、为AdoDatabse1.sdf。(5)点击添加,请注意这个数据库是一个新的空数据库,在点击了完成按钮之后需要设计和配置数据库。(6)该文件被添加到数据库项目中,并作为刚才选中的添加结点的孩子结点。例如,如果想在表结点中添加一个文件,那么该文件就作为这个结点的孩子结点。(7)在解决方案资源管理器中,单击新添加的文件(NewlyAddedFile)。(8)在视图菜单中,如果属性窗口没有显示,单击属性窗口。然后,你会看到属性设置,根据要求设置属性。24242.定义数据库模式定义数据库模式一旦在项目中创建了数据库项,就可以根据下面的步骤定义数据库模式:(1)在视图菜单中,如果没有显示服务资源管理器窗
13、口,应单击服务资源管理器。(2)在服务资源管理器中,你可以看到数据库AdoDatabase1已经在数据连接文件夹中创建。(3)右键单击数据库中的表文件夹,就会出现表模式,如图10.5所示。你可以定义表名、列名、类型和长度。如果允许数据为空,可以定义为空,如果要求数据不能重复,定义为主键。2525图10.5 给应用添加数据库2626(4)现在,可以把数据库和项目连接起来。在数据菜单中选择添加新的数据资源(AddNewDateSource)。(5)选择数据库并单击下一步。(6)选择你建立的数据库AdoDatabase1并单击下一步。(7)给数据集AdoDatabase1DataSet命名,然后单击
14、完成。27273.创建访问数据库的项目创建访问数据库的项目现在可以使用.Net菜单命令并且依据向导创建一个访问数据库的项目。(1)单击文件新建项目。(2)选择C#。(3)选择数据库项目。(4)给项目命名并确定它的路径。(5)选择SQLServer。(6)命名服务。2828现在,可以开始写读写SQL数据库的C#程序。类似地,访问磁盘文件时,需要在读写之前打开文件,完成后关闭文件,访问数据库步骤如下:(1)使用SqlConnection类实例化一个连接变量cn。(2)用cn.Open把连接变量绑定到数据库。2929(3)用sqlCo mmand类实例化一个命令变量。(4)初始化该命令以说明做什么。
15、(5)将该命令和连接变量关联起来。(6)调用命令变量的方法执行不同的数据库操作。例如:从数据库中读数据;删除数据;插入数据。(7)调用cn.Close方法关闭连接。图10.6说明了该过程中的前两步。用ADO.Net中的SqlConnection类实例化变量,它使用SQL的连接字符串并且把它存储在变量的数据域中。它也可以创建一个到数据库的点。3030图10.6 创建连接变量并打开连接31 31 图10.7阐述的是步骤(3),(4),(5)。用sqlCo mmand类实例化命令变量cm,并传送字符串“deletefromtitleswheretitle_idPL4321”作为命令的操作。然后,给c
16、m.Connection分配连接变量,把命令变量和连接变量关联起来。3232图10.7 向数据库发送命令字符串3333在.Net2008中,也可以通过以下步骤创建基于数据集的本地数据库或者是基于服务的数据库。(1)创建一个Windows表单应用,把它命名为myDBproject。(2)在解决方案资源管理器中,右键点击名为myDBproject的项目,选择添加新项;(3)在项列表中,选择基于数据集的本地数据库或者基于服务的数据库。下面的代码说明了上面讨论的步骤。3434SqlConnectioncnnewSqlConnection (serverlocalhost;databasemySqlDB
17、;uidjon;pwddoe123);trycn.Open();SqlCo mmandcmnewSqlCornmand();cm.Co mmandTextdeletefromtitleswheretitle_idPL4321;cm.Connectioncn;3535cm.ExecuteNonQuery();/Executetheco mmandwithoutareturnvaluecatch(SqlExceptionex)Console.WriteLine(ex.Message);finallycn.Close();3636上面的代码是从数据库删除一个数据。要向数据库插入一个数据,除了给命令变
18、量cm传送一个不同SQL字符串外,处理过程是相同的,代码是相通的。SqlCo mmandcmnewSqlCo mmand(insertintotitles(title_id,title,type,pubdate)values(JP1001,Progra mminginSOA,business,May2008),conn);3737这两个例子用ExecuteNonQuery()方法执行SQL命令,因为两个命令都不返回值。从数据库检索信息并得到返回值的数据库访问叫做查询。如果查询返回一个标量类型的值,可以使用ExecuteNonScalar()方法,如下面的代码所示:staticpublicint
19、AddProductCategory(stringnewName,stringcnStr)Int32newProdID0;3838stringcmStr INSERTINTOProduction.ProductCategory(Name)VALUES(Name);SELECT CAST(scope_identity()ASint);using(SqlConnectioncnnewSqlConnection(cnStr)SqlCo mmandcmnewSqlCo mmand(cmStr,cn);cm.Parameters.Add(Name,SqlDbType.VarChar);3939 cm.P
20、arametersname.ValuenewName;try cn.Open();newProdID(Int32)cm.ExecuteScalar();catch(Exceptionex)Console.WriteLine(ex.Message);4040return(int)newProdID;如我们在第6章所提到的,用Web.config文件存储大量的用户名和密码不容易管理。当存储的账户量很大时,需要使用数据库。下面的代码说明了如果用户名和密码已经存储在SQL数据库中,如何使用SQL的ExecuteScalar()命令验证它们。41 41voidmyLogIn(Objectsender,E
21、ventArgse)if (myAuthentication (UserName.Text,Password.Text)FormsAuthentication.RedirectFromLoginPage(UserName.Text,Persistent.Checked);else Output.TextInvalidloginnameorpassword;4242boolmyAuthentication(stringusername,stringpassword)SqlConnectioncnnewSqlConnection(serverlocalhost;databasemySqlDB;ui
22、djon;pwddoe123);try cn.Open();4343 StringBuilderbuildernewStringBuilder();builder.Append(selectcount(*)fromusers whereusername);builder.Append(username);builder.Append(andcast(rtrim(password)as varbinary)cast();4444 builder.Append(password);builder.Append(asvarbinary);SqlCo mmandcmnewSqlCo mmand (bu
23、ilder.ToString(),cn);intcount(int)cm.ExecuteScalar();return(count0);catch(SqlException)return4545 false;finally cn.Close();在这段代码中,方法由用户处取得用户名和密码,把它们按照SQL查询串的格式输入,然后它使用该串查询数据并检查用户名和密码是否在数据库中已经存在。如果存在,则验证成功;否则,访问被拒绝。注意,用户名和密码从不需要检索,这是一种访问用户名和密码的安全方法。4646很多情况下,数据库返回一个复杂的数据结构。这时,我们需要一个读方法按片提取数据切片。SqlCo
24、mmand类中的ExecuteReader方法一次读取流中的一个结点,返回一个SqlDataReader对象。SqlDataReader对象中有对结果集进行扫描的方法,它只可向前并且是只读的,这类似于第4章讨论过的XmlTextReader。每次调用DataReader.Read(),都返回结果集的一行。下面的代码是一个使用ExecuteReader方法的例子,它从数据库读一个结点,然后把结点的每个元素打印出来。4747privatestaticvoidCreateCo mmand(stringcmStr,stringcnStr)using(SqlConnectioncnnewSqlConne
25、ction(cnStr)cn.Open();SqlCo mmandco mmandnewSqlCo mmand(cmStr,cn);SqlDataReaderreaderco mmand.ExecuteReader();while(reader.Read()Console.WriteLine(String.Format(0,reader0);484810.2.3 ADO.Net中的数据适配器和数据集中的数据适配器和数据集如我们在第4章讨论过的,XmlDocument类在内存中创建一个XML文件的XML树。数据集类在内存中创建一个复杂操作的关系数据库的结构,这个结构不是一个树,而是一组表,每个表
26、包括很多行和列,每行或者每列包含很多元素。当访问一个Web服务时,经常会返回一个数据集对象。为了使数据集独立于设备(数据库),用数据适配器类将数据集和数据库链接起来。对不同类型的数据库,需要创建不同的数据适配器。图10.8说明了数据集、数据适配器和数据库。4949图10.8 数据集、数据适配器和数据库5050下面的方法说明了如何从SQL数据库中读取一个数据集合。该方法有四个参数:数据集引用、指定数据库的连接字符串、指定被执行命令的命令字符串、指定数据库中被选定行的字符串。usingSystem.Data.SqlClient;usingSystem.Data;classmyProgram51 5
27、1 staticpublicDataSetretrieveDataSet(stringcmStr,stringcnStr)DataSetdSetnewDataSet();SqlConnection cn new SqlConnection(cnStr);SqlDataAdapter adptr new SqlDataAdapter();adptr.SelectCo mmandnewSqlCo mmand(cmStr,cn);adptr.Fill(dSet);returndSet;5252 static public void writeBack(DataSet dSet,string cmSt
28、r,string cnStr)SqlConnectioncnnewSqlConnection(cnStr);SqlDataAdapteradptrnewSqlDataAdapter();adptr.SelectCo mmandnewSqlCo mmand(cmStr,cn);adptr.Update(dSet);staticvoidMain(stringargs)5353 stringmyCnStrserverlocalhost;databasemySqlDB;uidjon;pwddoe123;stringmyCmStrselect*fromtitles;stringmyRowtitles;D
29、ataSetmyDSetretrieveDataSet(myCnStr,myCmStr);DataTabletablemyDSet.Tablestitles;DataRowrowtable.NewRow();/InitializetheDataRow rowtitle_idJP1001;rowtitlePL1;5454 rowprice89.00;rowauthorMiller;rowpubdate2007;/AddtheDataRowtotheDataTable table.Rows.Add(row);myCmStrupdate*fromtitles;writeBack(myDSet,myC
30、nStr);5555该段代码包括了三个方法。它的功能是什么呢?首先,调用retrieveDataSet(myCnStr,myCmStr)方法执行下面的操作序列:(1)创建一个没有结构的数据集。(2)用传送到SqlDataAdapter构造函数的连接字符串打开一个数据库连接。(3)用传送到SqlDataAdapter构造函数的查询字符串执行数据库查询。(4)用和数据库中的“标题”项相匹配的模式初始化并结构化数据集dSet。5656(5)检索查询产生的所有的记录并将它们写入数据集dSet。(6)把dSet做为返回值返回。(7)关闭数据库的连接。然后,在Main方法中,将数据集传递给myDSet,就
31、可以访问其中的一个表、一行或一个元素。我们可以修改任何元素的值。在代码中,可以给数据集增加一行,并且按照所给值给元素赋值。修改过的数据集驻留在内存中。更新数据库writeBack方法,WriteBack方法执行如下操作:(1)用传送到SqlDataAdapter构造函数的连接字符串打开一个数据库连接。5757(2)将命令和连接变量关联起来。(3)更新数据库。如果想读、写或者修改多个数据项,从数据库检索一个完整的数据集并写回该数据集更为合理。也可以访问数据库中单个的数据项。下述来自MSDN的例子说明了如何使用SqlDataAdapter类和它的方法直接插入、删除和更新SQL数据库中的数据项。pu
32、blicstaticvoidCreateSqlDataAdapter()5858 SqlDataAdaptercustDAnewSqlDataAdapter(SELECTCustomerID,CompanyNameFROMCustomers,DataSourcelocalhost;IntegratedSecuritySSPI;InitialCatalognorthwind);SqlConnectioncustConncustDA.SelectCo mmand.Connection;custDA.MissingSchemaActionMissingSchemaAction.AddWithKey;
33、5959 custDA.InsertCo mmandnewSqlCo mmand(INSERTINTOCustomers (CustomerID,CompanyName)VALUES(CustomerID,CompanyName),custConn);custDA.UpdateCo mmandnewSqlCo mmand(UPDATECustomersSETCustomerID CustomerID,CompanyNameCompanyNameWHERECustomerID6060 oldCustomerID,custConn);custDA.DeleteCo mmandnewSqlCo mm
34、and(DELETEFROMCustomersWHERECustomerIDCustomerID,custConn);custDA.InsertCo mmand.Parameters.Add(CustomerID,SqlDbType.Char,5,CustomerID);custDA.InsertCo mmand.Parameters.Add(61 61CompanyName,SqlDbType.VarChar,40,CompanyName);custDA.UpdateCo mmand.Parameters.Add(CustomerID,SqlDbType.Char,5,CustomerID)
35、;custDA.UpdateCo mmand.Parameters.Add(CompanyName,SqlDbType.VarChar,40,CompanyName);custDA.UpdateCo mmand.Parameters.Add(oldCustomerID,SqlDbType.Char,5,CustomerID).SourceVersionDataRowVersion.Original;6262custDA.DeleteCo mmand.Parameters.Add(CustomerID,SqlDbType.Char,5,CustomerID).SourceVersionDataR
36、owVersion.Original;6363关系数据库在同质表、行和列中存储数据。在SOC中,很多数据文件是XML格式的,它是分层的并且在结构上是异质的。开发一个XML数据库查询语言的目标包括:(1)表达能力强:语言必须具有强大的表达能力,这样才能够表达复杂的查询功能。10.3 基于基于XML的数据库和查询语的数据库和查询语言言XQuery6464(2)简单:让用户能用简单的方式表达查询是一个很重要的问题。使用查询语言的人经常是一些缺乏编程经验的人。(3)有效:查询语言处理的是数据库,数据库通常是较大的并且存储在类似磁盘等二级存储器中。管理数据库的硬件能够有效地执行语言是最基本的要求,并且语
37、言必须为数据库和执行查询语言的程序之间的数据交换提供批处理模式。656510.3.1 查询的表达查询的表达XML数据库和关系数据库不同,它是树形结构的,树的分支的深度有可能是不平衡的。对于XML查询,它不检索数据集、表、行或者是列。它检索类于子树的项、结点的父结点(前驱结点)和所有后继结点(孩子结点)这样的项。XML查询也能够检索分散在XML树中不同部分的信息。例如,检索所有的具有年龄(age)属性并且该属性值是“60”的元素。我们在第4章讨论过的Xpath就能够完成这种查询:例如 /*age606666此外,XML数据库中的元素是有序的。我们能够找出XML数据库中某个元素的前驱结点和它的后继
38、结点;而关系数据库中的元素是无序的,你可按照自己希望的方式检索数据并且储存数据。由于关系型数据库和XML数据库的不同,开发XML查询语言就显得非常必要了。1998年,W3C赞助了关于XML查询的研究组织。67671999年,W3C启动了一个XML查询工作组。2001年,该工作组发布了最初的语言规范草案,该工作草案定期更新。2007年1月,XQuery1.0正式发布(http:/www.w3.org/TR/xquery/),XQuery成为XML家族的一个新成员。XQuery基于XML和Xpath,它共享了XML模式和XSLT样式转换的结构和语义。XQuery是一种程序设计语言,它支持通用数据类
39、型,例如:数值型、布尔型、字符串、日期、时间,持续时间和XML类型。6868XQuery也是一种函数型程序设计语言,它和Scheme与LISP有很多类似之处。像所有的函数型程序设计语言一样,每个XQuery语句都要计算结果并且返回一个值。例如:let$x:3$y:7return 8*$x4*$y它返回52。上面的例子类似下面的Scheme表达式:(let (x 3)(y7)(*8 x)(*4 y)6969)另一个XQuery的例子是:let$target:audiooutput,$content:beepreturn processinginstruction$target$contentXQ
40、uery的一个特点是提供了FLWOR表达式,它支持中间结果的扫描和变量的绑定。这种表达式经常用于两个或者更多文档的连接,以及数据的再结构化。FLWOR发音为“flower”(花),FLWOR是for、let、where、orderby和return的首字母缩写词。7070XQuery支持的结构和句子在下面的BNF注释中给出:FLWORExp (ForClause|LetClause)WhereClause?OrderByClause?returnExprSingleForClause for$VarName TypeDeclaration?PositionalVar?inExprSingle(
41、,$VarName TypeDeclaration?PositionalVar?in ExprSingle)*LetClause let$VarNameTypeDeclaration?:ExprSingle(,71 71$VarNameTypeDeclaration?:ExprSingle)*TypeDeclaration asSequenceTypePositionalVar at$VarNameWhereClause whereExprSingleOrderByClause (orderby)|(stableorderby)OrderSpecListOrderSpecList OrderS
42、pec(,OrderSpec)*OrderSpec ExprSingleOrderModifierOrderModifier7272 (ascending|descending)?(empty(greatest|least)?(collationURILiteral)?下面的例子来自http:/www.w3.org/TR/xquery/,包括很多XQuery结构和句子。for子句循环扫描输入文档的所有部门,变量$d轮流绑定到每个部门号。对于每个$d的绑定,let子句把变量$e绑定到给定部门的所有员工,员工是从另外一个输入文档选择的。for子句和let子句的结果是元组流,在该元组流中每个元组包括
43、一对绑定变量$d和$。where子句过滤元组流,仅保留至少有10个员工的部门的绑定对。order子句按部门员工平均工资的降序给保留的元组排序。return子句为保留元组构造一个新的bigdept元素,包含部门号、总人数和平均工资。7373for$dinfn:doc(depts.xml)/depts/deptnolet$e fn:doc(emps.xml)/emps/empdeptno$dwherefn:count($e)10orderbyfn:avg($e/salary)descendingreturn$d,fn:count($e),7474 fn:avg($e/salary)XQuery程序
44、可以潜入在Web文件中,也可以访问Web文件。下面的代码是一个潜入到HTML文件的例子,它读入Web上的一个文件作为输入。7575let$d doc(http:/asusrl.eas.asu.edu/services/xml/Courses.xml)/Coursefor$bin$d/Coursewhere$b/Level200orderby$b/Codereturn 7676$b/Name,$b/Level,$b/Cap 777710.3.2 XML文档的转换文档的转换上面的例子说明了如何使用XQuery程序指定标准并检索信息以满足标准。只要适当的指定标准,也能够用XQuery程序把一个文件转
45、换为另一个文件,它是第4章讨论的的一个功能。XQuery的官方站点(http:/www.w3.org/TR/xquery/)使用如下的例子说明这个转换过程。首先,假设有一个存储书籍信息的XML文档。7878 TCP/IPIllustrated Stevens AddisonWesley AdvancedProgra mmingintheUnixEnvironment Stevens AddisonWesley7979 DataontheWeb Abiteboul Buneman Suciu如果想要生成另外一个关于作者以及他所写的书的XML文档,可以使用下面的XQuery程序执行转换:8080f
46、or$ainfn:distinctvalues($bib/book/author)orderby$areturn$a for$bin$bib/bookauthor$aorder by$b/title return$b/title81 81 该程序的输出是转换过的XML文档,如下所示:Abiteboul8282 DataontheWeb Buneman DataontheWeb 8383 Stevens AdvancedProgra mming intheUnixEnvironment TCP/IPIllustrated Suciu8484 DataontheWeb 858510.3.3 XQu
47、ery讨论讨论XQuery被定义为通用的程序设计语言,有表达并行和顺序查询的附加的结构和子句。语言定义保证了在面向服务的软件开发中建立面向服务的数据库。该语言基于已建立良好的函数程序设计范型,该范型为语言提供了坚实的数学基础和应用背景。其他XML技术,例如SAX和DOM模型,提供了对XML数据库的流访问和批处理访问。Xpath语法和语义及其应用技术能够简单地导入到该语言中。然而,XQuery的成功也依赖于检索、修改、转换大型数据库的高效算法和并行处理能力的开发。这是一个仍需解决的难题。8686LINQ是一个说明性的、强类型的程序设计语言,它基于函数程序设计范型和表达式。本节简要讨论LINQ,并
48、使用该语言管理和查询不同的数据源。本节中也给出了开发过程和可运行的程序。10.4 综合查询语言综合查询语言LINQ878710.4.1 LINQ的目标的目标LINQ是一个针对不同数据实体的统一程序模型,包括ADO.Net数据实体、XML结点和关系数据库记录。LINQ的主旨是定义一套标准的语言结构,这些结构包括在不同的面向对象程序设计语言中,所以这些程序设计语言只能处理和LINQ一组统一的内存数据对象。LINQ实现将处理不同的数据源,如图10.9所示。从程序员的观点看,他们学习的是如何写访问内存对象的LINQ程序。同样的程序也能访问ADO数据实体、SQL数据记录和XML文件。8888如果没有LI
49、NQ,要访问不同的数据源,开发人员需用不同的程序设计语言写不同的程序。LINQ能嵌入到不同的面向对象程序设计语言中。目前,它在C#和VB中使用,但可以扩展到其他程序设计语言中。8989图10.9 数据适配器和数据集909010.4.2 表达式表达式LINQ基于表达式。为了学习LINQ,首先简要介绍表达式。表达式结构简单,在词汇级上,只有三个词汇单元:、圆括号“(”和“)”、无穷变量列表(名称),例如,a,b,a1,a2,等等。在语法级上,表达式是词汇单元和变量的有限组合。使用BNF符号,一个简单的表达式可以定义为:expression|91 91|()|()在定义中,常量是任意数据类型的值。变
50、量(标识符)和表达式的定义已经在第1章论述过。根据定义,下列表达式都是表达式。5 ;aconstantisexpressionx;a variableisexpression9292xy;an expressionisexpression3x(xy);()isexpression3x(xy)5;()isexpression上面的表达式,叫做“过程”或“方法”。前缀变量叫做方法的参数,参数后的表达式叫做方法体。参数的作用范围是方法体。例如,我们有一个表达式:93933x(xy)(x3)x中参数x的作用范围是(xy),它不包括(x3)中的x。在表达式中,x若在其作用范围内出现,则称为约束出现。如果