中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> IBM专区 > DB2 > .NET technology
服务访问 DB2 Universal Database 表
作者:佚名 时间:2005-09-09 15:23 出处:互连网 责编:小渔
              摘要:使用在 Delphi 8 for .NET 中编写的 ASP.NET Web 服务访问 DB2 Universal Database 表
Bob Swart
顾问, Bob Swart Training & Consultancy
2004 年 5 月
本文展示了如何使用 Delphi 8 for .NET 来构建 ASP.NET Web 服务应用程序,该应用程序将向外界发布 DB2 UDB SAMPLE 数据库中的数据表。

简介
在这篇文章中,我将演示如何使用 Borland® Delphi™ 8 for the Microsoft® .NET Framework(以下缩写为“Delphi 8 for .NET”)来构建 ASP.NET Web 服务应用程序,该应用程序将发布 IBM® DB2® Universal Database™(UDB)SAMPLE 数据库中的数据表。

除了构建 ASP.NET Web 服务,我还将展示如何用 Delphi 8 for .NET 构建 Windows Forms 客户机应用程序,该应用程序将使用 ASP.NET Web 服务远程访问 UDB SAMPLE 数据库表。

在后续文章中,我将介绍一些通过扩展 ASP.NET Web 服务的数据库更新能力而获得的增强功能,以允许客户机通过该 ASP.NET Web 服务来发送更新。

创建 ASP.NET Web 服务
Web 服务可用于相互连接不同的应用程序(例如,B2B 事务),或者用于将一个应用程分裂成一个多层的或分布式的应用程序(一个多层应用程序的各个层在不同的机器上运行)。本文中,我将构建一个连接 UDB SAMPLE 数据库的 ASP.NET Web 服务,用以对外界以及使用该 ASP.NET Web 服务来访问真正数据库表的客户机屏蔽实际的数据库连接细节。

ASP.NET 是 .NET Framework 的组成部分,您可以用 Delphi 8 for .NET(或 Borland® C#Builder™)来构建 ASP.NET Web 服务。因此,启动 Delphi 8 for .NET,执行 File | New - Other,进入带有新应用程序可用目标的 Object Repository。选择 ASP.NET 区域中的 ASP.NET Web Service Application目标。该选择将产生新的 ASP.NET Application 对话框,如下图 1 所示。

图 1. New ASP.NET Application
图 1. New ASP.NET Application

这里指定的项目名称还将用作开发机器上虚拟目录的名称(还需要在实际部署的 Web 服务器上创建一个相似的虚拟目录,您可以在这个 Web 服务器上安装该 Web 服务)。例如,我将使用名称 DB2SAMPLE。

请注意,我已选择 Internet Information Server(IIS)作为进行测试的 Web 服务器。如果部署机器无法使用 IIS(例如,在使用 Windows XP 家庭版的情况下),也可以使用 Cassini 这类免费的个人 Web 服务器来测试 Web 服务。

标识 Web 服务
当您单击 OK时,将创建一个新的 ASP.NET Web Service 项目。它包含文件 WebService1.pas 和 .asmx,您可能需要将其重新命名为 DB2WebService。结果文件 DB2WebService.pas 包含实际的 TWebService1 类型的 Web 服务。这将是向外界,包括您的客户机所发布的名称,因此,您应该使用一个更具描述性的名称。通过全局查找和替换,您可以将 TWebService1 重新命名为诸如 TDB2Data 的名称。请注意,T 前缀实际上是从 Delphi 开发者社区中遗留下来的(“T”代表“Type”)。如果您想要与 .NET 世界相融合,就可以决定不使用该前缀,您也可以使用它并且为使用 Delphi 而感到自豪。

除了 Web 服务的名称,还应该给它添加一个惟一的名称空间。如果人们需要使用您的 Web 服务(本例中是 TDB2Data),并且碰巧遇到了另一个具有相同名称的 Web 服务,这就尤其重要。这种情况未必会发生,但也并非是不可能的。Web 服务的名称和名称空间的结合使得后者不可能发生 —— 特别是如果该名称空间基于您的公司名,比如本例中的 eBob42。

可以将该名称空间定义为 WebService 属性的特性,如下:


            [WebService(Namespace='http://www.eBob42.org',
            Description='IBM DB2 UDB SAMPLE Database Tables')]
            

WebService 属性的描述特性用于进一步阐明该 Web 服务的使用。

定义 Web 服务
Web 服务可以包含许多方法,但是只有标以 [WebMethod] 属性的方法才真正作为 Web 服务方法而可见,且可以从外界进行调用。Delphi 8 for .NET 所生成的 Web 服务项目中已经存在了一个示例方法 HelloWorld(位于注释中)。然而,您并不需要该示例方法,所以要删除它(还要从实现中删除它),并向 TDB2Data Web Service 类添加两个自己的方法定义,如下:


            [WebService(Namespace='http://www.eBob42.org',
            Description='IBM DB2 UDB SAMPLE Database Tables')]
            TDB2Data =
            class(System.Web.Services.WebService)
            ...
            public
            
     constructor Create; { Generic BdpConnection interface } [WebMethod] function GetTableNames: String; [WebMethod] function GetDataSet(TableName: String; start, max: Integer): DataSet; end;

方法 GetTableNames 将用于在一个用逗号分隔的字符串中返回 DB2 UDB SAMPLE 数据库中可用的一系列表名。方法 GetDataSet 将使用所传递的表名来执行“select * from tablename”,并返回一个数据集,该数据集中包含从该查询结果集中的 start 位置开始的 max 条记录。因此,您实际可以使用该方法来获取前 10 条记录,接下来的 10 条记录等等。

连接 DB2
这两个 Web 方法都需要连接 UDB SAMPLE 数据库,才可以产生结果。这意味着需要一个 BdpConnection 组件来共享该连接。可以将它添加到 TDB2Data Web 服务定义中,也可以更为省力地移至 Web 服务的设计视图,然后在上面放置该 BdpConnection 组件。实际上,不要简单地从 Tool Palette 放置 BdpConnection 组件,我建议您使用 Data Explorer(位于 Delphi 8 for .NET IDE 的右上角),并将已经配置为连接 UDB SAMPLE 数据库的 DB2Connection 组件拖放至设计视图上(如果您需要将 BdpConnection 组件连接 UDB SAMPLE 数据库的支持,请阅读 用 Delphi for .NET 访问 DB2 Universal Database 表)。

实现 Web 服务
设置好连接 DB2 的 BdpConnection 之后,就可以实现刚才定义的这两个 Web 方法了。首先,需要向(实现部分中的)uses 子句添加 System.TextBorland.Data.Schema 这两个单元。第一个用于 StringBuilder 类,而第二个用于 ISQLMetaData 定义。

方法 GetTableNames 可以调用 BdpConnection 组件的 GetMetaData 函数,以获得元数据信息。该元数据包含一个函数,基于名称或指定类型返回表名。GetTables 函数的结果实际上就是一个数据表(DataTable)。然后,您需要遍历该 DataTable 的各行,并且查看第三个字段(带有真正的表名),以及向一个 stringbuilder 类追加该表名。其完整代码如下:


            function TDB2Data.GetTableNames: String;
            var
            MetaData: ISQLMetaData;
            Tables: DataTable;
            S: StringBuilder;
            i: Integer;
            begin
            BdpConnection1.Open;
            try
            MetaData := BdpConnection1.GetMetaData;
            Tables := MetaData.GetTables(', TableType.Table);
            S := StringBuilder.Create;
            for i:=0 to Tables.Rows.Count-1
            do
            
     begin S.Append(Tables.Rows[i].Item[3, DataRowVersion.Current]); if i < Tables.Rows.Count-1 then S.Append(',') end; Result := S.ToString finally BdpConnection1.Close end
end;

请注意,没有异常处理(除了 try-finally 块),所以如果发生错误,就将返回异常错误消息,而非一系列的可用表名。

另一个方法 GetDataSet 接收一个由 GetTableNames 返回的表名,并且用它来产生“select * from”表名的查询。通过将该查询作为参数,您可以创建 BdpDataAdapter 组件的一个实例,并且用它来填充(Fill)该方法所返回的 DataSet。BdpDataAdapter 的 Fill 方法允许您选择指定返回记录的起始位置和最大数目,您也可以在其中使用 start 和 max 参数。其完整代码如下:


            function TDB2Data.GetDataSet(TableName: &String; start, max: Integer): DataSet;
            var
            DataAdapter: BdpDataAdapter;
            begin
            Result := DataSet.Create;
            DataAdapter := BdpDataAdapter.Create('select * from ' + TableName,
            BdpConnection1);
            BdpConnection1.Open;
            try
            DataAdapter.Fill(Result, start, max, TableName)
            finally
            BdpConnection1.Close;
            DataAdapter.Free
             
            end
            
end;

请注意,在创建 BdpDataAdapter 之前,您不必打开 BdpConnection,而是刚好在用 DataSet 中所指定的名称调用 Fill 方法以产生 DataTable 之前打开,而该 DataSet 是作为一个函数结果返回的。

再次考虑 Web 服务
上面的两个方法可以在本地浏览器中进行测试,并且将工作良好。但是在实际环境中,第二个方法有点太危险,以致不允许将它发布。将表名(TableName)作为参数传递给要执行 SQL 语句的函数简直是自讨苦吃。这很容易将“EMPLOYEE; DROP TABLE”作为表名来传递,从而试图将一条 SQL 语句变成两条。当您允许 Web 服务客户机传递将要插入 SQL 语句中的文字表名时,这还只是所要面对的安全问题中的一个例子。

实际上,您需要提供访问表 Y 中从位置 Z 开始的 X 条记录的能力,但是首先不能放弃数据库的安全性。显而易见的解决方案就是调用第一个 Web 方法来返回 TableName 列表,然后用这些表名来产生指定的方法,这些方法将被绑定(硬编码)到那些表名上。对于 UDB SAMPLE 数据库,表名为 CL_SCHED、DEPARTMENT、EMP_ACT、EMP_PHOTO、EMP_RESUME、EMPLOYEE、IN_TRAY、ORG、PROJECT、SALES 和 STAFF。

这将在 TDB2Data Web Service 类中产生下列 11 个新方法定义(请注意,我已经缩写了 WebMethod 属性的描述特性以便于阅读):


            { Specific DB2 UDB SAMPLE Database interface }
            [WebMethod(Description='Return the CL_SCHED table, ...')]
            function GetCL_SCHED(start, max: Integer): DataSet;
            [WebMethod(Description='Return the DEPARTMENT table, ...')]
            function GetDEPARTMENT(start, max: Integer): DataSet;
            [WebMethod(Description='Return the EMP_ACT table,  ...')]
            function GetEMP_ACT(start, max: Integer): DataSet;
            [WebMethod(Description='Return the EMP_PHOTO table,  ...')]
            function GetEMP_PHOTO(start, max: Integer): DataSet;
            [WebMethod(Description='Return the EMP_RESUME table,  ...')]
            function GetEMP_RESUME(start, max: Integer): DataSet;
            [WebMethod(Description='Return the EMPLOYEE table,  ...')]
            function GetEMPLOYEE(start, max: Integer): DataSet;
            [WebMethod(Description='Return the IN_TRAY table,  ...')]
            function GetIN_TRAY(start, max: Integer): DataSet;
            [WebMethod(Description='Return the ORG table,  ...')]
            function GetORG(start, max: Integer): DataSet;
            [WebMethod(Description='Return the PROJECT table,  ...')]
            function GetPROJECT(start, max: Integer): DataSet;
            [WebMethod(Description='Return the SALES table,  ...')]
            function GetSALES(start, max: Integer): DataSet;
            [WebMethod(Description='Return the STAFF table,  ...')]
            function GetSTAFF(start, max: Integer): DataSet;
            

除了添加这 11 个新方法外,还应该确保从 GetDataSet 方法的前一行删除 [WebMethod] 属性,因为我们认为向外界发布该方法太过危险。但是,您不应从 Web 服务自身完全删除该方法,而是应该将它保留为一个“内部”方法。这 11 个新方法将乐于调用它来产生各自的结果,并且这次是安全的,因为表名的传递是由 Web 服务自身完成的,如以下实现所示:


            { Specific DB2 UDB SAMPLE Database interface }
            functionTDB2Data.GetCL_SCHED(start, max: Integer): DataSet;
            begin
            Result := GetDataSet('CL_SCHED', start, max)
            end;
            

function TDB2Data.GetDEPARTMENT(start, max: Integer): DataSet; begin Result := GetDataSet('DEPARTMENT', start, max) end;

function TDB2Data.GetEMP_ACT(start, max: Integer): DataSet; begin Result := GetDataSet('EMP_ACT', start, max) end;

function TDB2Data.GetEMP_PHOTO(start, max: Integer): DataSet; begin Result := GetDataSet('EMP_PHOTO', start, max) end;

functionTDB2Data.GetEMP_RESUME(start, max: Integer): DataSet; begin Result := GetDataSet('EMP_RESUME', start, max) end;

function TDB2Data.GetEMPLOYEE(start, max: Integer): DataSet; begin Result := GetDataSet('EMPLOYEE', start, max) end;

function TDB2Data.GetIN_TRAY(start, max: Integer): DataSet; begin Result := GetDataSet('IN_TRAY', start, max) end;

function TDB2Data.GetORG(start, max: Integer): DataSet; begin Result := GetDataSet('ORG', start, max) end;

function TDB2Data.GetPROJECT(start, max: Integer): DataSet; begin Result := GetDataSet('PROJECT', start, max) end;

function TDB2Data.GetSALES(start, max: Integer): DataSet; begin Result := GetDataSet('SALES', start, max) end;

function TDB2Data.GetSTAFF(start, max: Integer): DataSet; begin Result := GetDataSet('STAFF', start, max) end;

正如您可以看到的,在内部仍然需要 GetDataSet 方法,以保持源代码的可理解性和可维护性。

测试 Web 服务
现在,保存您的工作,编译该应用程序,并从 Delphi 8 for .NET IDE 中测试它。可以通过 Run 菜单来完成该工作——要么调用 Run | Run(带有调试器),要么更快地调用 Run | Run without Debugging。不管哪种方法都将用该 Web 服务的 URL 产生默认的 Web 浏览器,展示您现在必须远程访问 UDB SAMPLE 数据库的 12 种方法。

图 2. 测试 TDB2Data Web 服务
图 2. 测试 TDB2Data Web 服务

例如,您可以单击 GetEMPLOYEE 方法,这将允许您用 start 和 max 参数的示例值调用该方法。请注意,该测试只能在本地进行(Web 服务驻留在本地主机上),除非您编辑了 Web.config 文件,那样也允许远程连接来测试 Web 服务。

图 3. 测试 GetEMPLOYEE 方法
图 3. 测试 GetEMPLOYEE 方法

可以给 start 和 max 指定某些值,然后单击 Invoke 按钮获得带有“select * from EMPLOYEE”查询内容的新页面,该查询起始于 start并返回 max条记录。

当您已验证该方法按计划执行时,就可以构建客户机应用程序,连接该 ASP.NET Web 服务,访问 UDB SAMPLE 数据库表。

构建 Web 服务客户机
您需要为 Web 服务客户机启动一个新项目,例如一个 WinForms 应用程序。执行 File | New Windows Forms Application新建一个。在 DB2WSClient中保存它,并且右击 Project Manager 中的项目节点,以选择 Add Web Reference菜单选项。这将允许您给项目添加到 Web 服务的引用 —— 帮助您导入和使用(消费)TDB2Data Web 服务。

添加 Web 引用
Add Web Reference 选项将产生一个相同名称的对话框,通过该对话框,您可以用 UDDI(通用描述、发现和集成,Universal Description Discovery and Integration)来定位 Web 服务;如果您知道其 WSDL(Web 服务描述语言,Web Service Description Language)的位置,也可以通过直接到该 Web 服务的 URL 来定位 Web 服务。

图 4. 添加 Web 引用
图 4. 添加 Web 引用

在本例中,TDB2Data Web 服务的 WSDL 位置是本地部署机器上的 http://localhost/DB2SAMPLE/DB2WebService.asmx?WSDL,因此,要输入该位置并按回车(或单击蓝色箭头),使 Add Web Reference对话框列出 WSDL,并启用该对话框右下角的 Add Reference 按钮:

图 5. 添加 WSDL 的 Web 引用
图 5. 添加 WSDL 的 Web 引用

单击 Add Reference 按钮,该 WSDL 就会被解析,生成一个特殊的导入单元并添加到您的 Delphi 8 for .NET 项目中。

图 6. Web 引用
图 6. Web 引用

使用 Web 服务
一旦添加了 Web 引用,您就可以真正使用它了。首先,向 WinForms 应用程序的 uses 子句添加单元 localhost.DB2WebService。这将确保您可以看到并使用所生成的导入单元中的类定义 TDB2Data。请注意,虽然该类名与您在 ASP.NET Web 服务中定义的类名相同,但它实际上是将与真正的 ASP.NET Web 服务进行通信的代理类。但是,就客户机应用程序而言,就好像显示一个本地对象,而且可以将这些方法作为本地方法来进行调用。

为了在客户机应用程序中使用 TDB2Data Web 服务,我已经在 WinForm 定义中添加了一个 TDB2Data 类型的私有字段,称作 DB2SAMPLE。该字段将保存 Web 服务代理的实例,并在 WinForm 的 Load 事件中用一行代码来创建,如下:


            procedure TWinForm.TWinForm_Load(sender: System.Object; e: System.EventArgs);
            begin
            DB2SAMPLE := TDB2Data.Create
            end;
            

现在,您就可以使用 DB2SAMPLE 字段并且在需要时调用诸如 GetEMPLOYEE 的方法了。

连接 DB2 EMPLOYEE 表
为了真正显示数据,要放置一个 DataGrid 控件。通常,在使用 Borland Data Provider(BDP)组件时,您会在设计时将 DataGrid 连接到一个 DataSet。但是这次,您没有一个可用的 DataSet。此外,客户机应用程序甚至不使用 Borland Data Provider for .NET。实际上,客户机应用程序是一个真正瘦客户机的应用程序,因为它甚至不需要(想要)特定于 DB2 的数据库驱动程序。该客户机只是调用 DB2SAMPLE Web 服务来获得一个本机 .NET 数据集,这可以来自于 UDB SAMPLE 数据库,也可以来自于针对该问题的任何其他数据库。Web 服务是这个多层应用程序体系结构中惟一使用特定于 DB2 数据库驱动程序的一层。

DataGrid的右下方,放置三个按钮,分别称作 btnFirstbtnNextbtnPrev。这些按钮将使用 DB2SAMPLE Web 服务和调用 GetEMPLOYEE 方法,每个获取 10 条记录。第一个将获得前 10 条记录,第二个将获得接下来的 10 条记录,而最后一个将获得再往前的 10 条记录。这意味着您需要带有 CurrentRecord 值的计数器,因此,还要将其作为一个私有字段添加在 WinForm 类定义中。

然后,您就可以实现 btnFirst、btnNext 和 btnPrev 按钮的三个 Click 事件了,如下:


            procedure TWinForm.btnFirst_Click(sender: System.Object; e: System.EventArgs);
            begin
            CurrentRecord := 0;
            DataGrid1.DataSource := DB2SAMPLE.GetEMPLOYEE(CurrentRecord,10);
            DataGrid1.DataMember := 'EMPLOYEE'
            end;
            
procedure TWinForm.btnNext_Click(sender: System.Object; e: System.EventArgs); begin CurrentRecord := CurrentRecord + 10; DataGrid1.DataSource := DB2SAMPLE.GetEMPLOYEE(CurrentRecord,10); DataGrid1.DataMember := 'EMPLOYEE' end;
procedure TWinForm.btnPrev_Click(sender: System.Object; e: System.EventArgs); begin CurrentRecord := CurrentRecord - 10; if CurrentRecord < 0 then CurrentRecord := 0; DataGrid1.DataSource := DB2SAMPLE.GetEMPLOYEE(CurrentRecord,10); DataGrid1.DataMember := 'EMPLOYEE' end;

请注意,我直接将 GetEMPLOYEE 方法的结果赋值给了 DataGrid 的 DataSource 属性,然后指定 DataTable 的名称(本例中,被设置为实际的表名 EMPLOYEE)。CurrentRecord 字段将维护 DataGrid 中第一条记录的索引,因此,您可以一页页地进行定位。

显示 DB2 数据
现在是编译和运行该应用程序的时候了。它首先将显示一个空的网格和三个按钮。但是单击第一个按钮时,它将连接 DB2SAMPLE Web 服务,并且检索前 10 条记录。

图 7. 瘦客户机中的前 10 条记录
图 7. 瘦客户机中的前 10 条记录

单击 NextPrev 按钮将分别显示另外的 10 记录。请注意,每当指派 DataGrid 的 DataSource 属性时,就会忽略 DataSource 的前一值,因此在给定时刻,您最多只会看到 10 记录 - 决不超过 10 条(当然,如果最后一页未包含 10 条记录,您看到的可能更少)。

要记住许多事情:WinForms 客户机应用程序目前只从 DB2SAMPLE Web 服务中检索数据。这意味着虽然您可以修改 DataGrid 中的数据,但是您无法将这些修改送回给 Web 服务或 UDB SAMPLE 数据库表。我将在下一篇文章中加以实现,它同时扩展了 DB2SAMPLE Web 服务和 WinForms 客户机应用程序。作为让终端用户知道 DataGrid 仅保存“只读”数据的变通方法,您应该将 DataGridReadOnly属性设置为 True(这将首先防止在客户端进行的修改)。

另一件值得注意的事情是,Web 服务以一种确保客户机(应用程序)不直接访问数据库的方式,将 WinForm 客户机和 UDB 数据库完全隔离。您可能需要考虑的惟一事情就是添加一个验证层 DB2SAMPLE Web 服务,因为任何人都可以连接该 Web 服务来检索 SAMPLE 表中的数据。本例中,数据是公有的(不是保密或敏感的),但是如果您处理的是敏感数据,就必须添加某种类型的保护。

结束语
本文中,我已经展示了如何使用 Delphi 8 for .NET 来构建 ASP.NET Web 服务应用程序,该应用程序向外界发布 DB2 UDB SAMPLE 数据库中的数据表。我还展示了如何用 Delphi 8 for .NET 构建 Windows Forms 客户机应用程序,使用该 ASP.NET Web 服务来远程访问 DB2 UDB SAMPLE 数据库表,在 DataGrid 中显示数据,以及通过请求包含 10 条记录的下一页面和前一页面在网格中进行定位。

在接下来的一篇文章中,我将介绍一些通过扩展 ASP.NET Web 服务的数据库更新能力而获得的增强功能,从而允许客户机通过该 ASP.NET Web 服务来发送更新。再见!

关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有