中国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
ADO.NET 和 Borland Data Provider
作者:佚名 时间:2005-09-07 11:54 出处:互连网 责编:小渔
              摘要:从 BDE 和 dbExpress 转移到 ADO.NET 和 Borland Data Provider
Jeremy McGee
咨询公司
2003 年 10 月
Borland Data Provider 经过改进的特性为那些需要访问 IBM DB2 的 .NET 开发人员提供了一种高效的、可伸缩的创建 Windows 客户机和 Web 应用程序的方法。在本文中,Jeremy McGee 通过使用 C#Builder Enterprise、DB2 UDB 和用于 Microsoft .NET Framework 的 IBM Managed Data Provider 查看 ADO.NET 类和接口。

简介
如果您使用过 Borland® Delphi、C++Builder 或 Kylix,您就会知道为了连接到像 IBM® DB2® Universal Database TM (UDB) 这样的数据库,最方便的是使用来自 Borland Visual Component Library (VCL) 的一些组件。有了 Borland C#Builder TM,一切都变了:现在不需要使用 Borland VCL,而是使用 Microsoft® .NET Framework,特别是 ADO.NET,而且也不需要使用 Borland Database Engine 或 dbExpress。

Bob Swart 关于连接到 DB2 UDB 数据的两篇文章说明了通过 C#Builder 可以使得创建简单的使用 Borland Data Provider (BDP) 和 ADO.NET 的数据库应用程序变得很容易。( 第一篇文章 展示了如何可视化地连接 BDP 组件以便创建简单的用户界面。 第二篇文章 则更详细地介绍了 DataSet 类。)

在本文中,我们将看看所有那些 ADO.NET 类和接口可以为我们做些什么。我们将使用 C#Builder Enterprise、DB2 UDB 和用于 Microsoft .NET Framework 的 IBM Managed Data Provider。

注意,我们将使用 DB2 管理的代码提供者作为一些示例。要安装该代码管理者,您需要 DB2 UDB 8.1 FixPak 3 或更佳版本的 Windows 客户机,该客户机要安装 Microsoft .NET Framework DB2 UDB 驱动程序。如果您现在使用的是 C#Builder,您将需要添加 IBM.Data.DB2 程序集(assembly)——使用 Project | Add Reference,然后选择 Browse 并导航到 IBM DB2 .NET Framework 1.1 程序集。默认情况下这个程序集安装在 C:\\Program Files\\IBM\\SQLLIB\\BIN\\netf11 中。必要时,我将使用常规的 DB2 UDB 样本数据库。

使用 ADO.NET:可视化还是非可视化?
VCL 用户马上会碰到的一个主要挑战就是在 .NET Framework 中没有相应的数据模型。这意味着在使用用户界面的可视情况下,要从其他格式的对象引用某个对象时没有容易的方法。虽然也有许多可以替代的选择,但是它们都要依赖于代码。在本文中,我包含了一些代码示例,以启示您该做些什么。幸好,与 VCL 一样,ADO.NET 给人印象深刻的一个方面是非常容易通过程序代码创建数据库访问逻辑。这些代码示例都将使用 C#。一旦您理解了程序代码(尤其是接口和类的精妙之处),则编写用于数据库访问逻辑的“包装器”类就非常简单了。如果以适当的方式编写了包装器类,则最终得到的将是“业务逻辑”类,这些业务逻辑类既可以处理所有的数据库访问,也可以在整个应用程序中使用。关于这一点的一个好例子就是我们在 前一篇文章 中讨论过的 IBuySpy 应用程序。

简短的概述
下面是经过简化的 ADO.NET 的架构视图,图中将 ADO.NET 与 BDE 作了比较。

图 1. ADO.NET 与 BDE 比较的简单视图
Figure 1. A simple view of how ADO.NET compares with the BDE

如果您熟悉 VCL 组件,那么您将发现 ADO.NET 背后的原理相当简单。ADO.NET 看上去虽然很不同,但是大部分的特性都还在——只不过是放在不同的地方而已。

与 DBConnection 连接
第一个类返回一个数据库连接对象。所有其他组件都要用这个对象作为句柄,以便引用数据库连接。

例如,在 C# 语言中,对于使用 DB2 管理的提供者的 IBM DB2:


            DB2Connection myConn = new
            IBM.Data.DB2.DB2Connection("DATABASE = SAMPLE;
            UID=<username>; PWD=<password>;");
            

一旦该句柄被赋值,便可以打开该句柄,使用它作为查询或存储过程的 Connection 属性,然后再关闭它。例如:


            DB2Connection myConn = new
            IBM.Data.DB2.DB2Connection("DATABASE = SAMPLE;
            UID=<username>; PWD=<password>;");
            string myInsertQuery = "INSERT INTO STAFF (ID,
            NAME) Values(1002,"Jane Doe")";
            DB2Command myComm = new DB2Command(myInsertQuery);
            myComm.Connection = myConn;
            myConn.Open();
            myComm.ExecuteNonQuery();
            myConn.Close();
            

我们将在后面讲述 DB2Command 对象。

很直观地,我们可以看到 ADO.NET 和 VCL 之间的一个重要区别。假如要使用 BDE 连接到一个数据库,我们最好使用一个 TDatabase 组件,而不管底层数据库发生了什么。至于 ADO.NET,我们为每种数据库服务器使用数据访问组件的一个专门化的变种。

不同的数据提供者是如何实现的
为每种类型的数据库服务器专门定制数据库访问类这种方法是 ADO.NET 的设计的主要特性之一。尽管驱动程序类各不相同,但是所有这些类都有相同的基本功能,通常可以交换使用。诀窍是通过定义了特定于数据库的类的 接口来实现。

使用 IBM.Data.DB2 管理的提供者访问 DB2 服务器上数据的应用程序将使用一个 DB2Connection 组件,上一个例子中便是如此。对于 SQL Server,有一个类似的组件,即 SqlConnection:


            SqlConnection myConn
            = new
            System.Data.SqlClient.SqlConnection("Persist
            Security Info=False;Integrated
            Security=SSPI;database=northwind;server=mySQLServer");
            
对于 Oracle,有一个 OracleConnection 组件:

            OracleConnection myConn
            = new
            System.Data.OracleClient.OracleConnection("Data
            Source=Oracle8i;Integrated Security=yes");
            

所有这些组件都有相同的一套基本属性、方法和事件:换句话说,这些组件共享相同的接口。用于数据库连接的接口是 IDbConnection,在 Microsoft .NET Framework 帮助中对此有文档说明。这个接口定义了最小级别的功能;实际上,每个组件都很可能有附加的属性、方法和事件,用以支持特定数据库或驱动程序的一些特殊的特性。

例如,DB2Connection 组件包括一个附加的方法,该方法可用于强制释放连接池资源。默认情况下,ADO.NET 提供者可以共享和重用连接,这样可以节省资源,但同时也意味着连接要一直开放,以备可能的重用。这个方法为您提供了更多的控制,这是通过像 SQL Server 管理的提供者这样的驱动程序所不能提供的。

虽然特定于数据库的组件提供了很大的灵活性,但是它们并不能带来可移植的代码。或许正是这个讨厌的特点使得您想从一种数据库换到另一种数据库。考虑到这一点,Borland 创建了 Borland Data Provider (BDP)。

BDP 是一个常规的受管 .NET 数据提供者,但是又有一点不同。它不是只使用一种数据库,而是可以使用多种不同的服务器。实际的驱动程序由创建 BdpConnection 对象时传递给这个对象的连接字符串(或者 ConnectionString 属性)决定。

所以为了使用 BDP 连接到 SQL Server,您可以使用:


            BdpConnection myConn = new
            Borland.Data.Provider.BdpConnection("assembly=Borl
            and.Data.Mssql,Version=1.1.0.0,
            Culture=neutral,PublicKeyToken=91d62ebb5b0d1b1b;ve
            ndorclient=sqloledb.dll;
            osauthentication=False;database=<database>;usernam
            e=<user>;hostname=<host>;
            password=<password>;provider=MSSQL");
            

注意,对于这种特定的驱动程序,主机名嵌入在连接字符串中。对于 DB2 驱动程序来说这不是必需的,您可以使用:


            BdpConnection myConn = new
            Borland.Data.Provider.BdpConnection("assembly=Borl
            and.Data.Db2,Version=1.1.0.0,
            Culture=neutral,PublicKeyToken=91d62ebb5b0d1b1b;ve
            ndorclient=db2cli.dll;
            database=<database>;username=<user>;
            password=<password>;provider=DB2");
            

虽然连接语法稍微有点不同,但是返回的对象是一样的。也就是说, 所有其他的代码并没有变。您甚至可以将这个字符串存放在一个资源文件中,并在运行时引用它。

Borland 提供了一个 Connections Editor,窗体设计者可以用它来创建连接字符串。我可以在 C#Builder 的另一个实例中另外创建一个项目,将一个 bdpConnection 对象放到 Windows Form 上,然后使用剪切板将连接字符串复制到我正在编写的应用程序的一个资源文件中。

在大多数情况下,BDP 在内部调用与特定于服务器的组件一样的本地客户机库,因此速度是一样快的。

DbConnection 类与 BDE TDatabase 或 dbExpress TSQLConnection 组件大致相当。与这些组件一样,DbConnection 是管理事务的地方。不过事务管理的方法却颇有不同:DbConnection 类的 BeginTransaction 方法返回一个事务对象,这个对象可用作后面的 SQL 命令的一个属性。

使用 DbCommand 执行查询
一旦打开了连接,ADO.NET 将通过组件直接运行对数据库的查询。与 DbConnection 类一样,DbCommand 是一个抽象接口,有其特定于服务器的实现。我们已经见过这个例子了:


            DB2Connection myConn = new
            IBM.Data.DB2.DB2Connection("DATABASE = SAMPLE");
            string myInsertQuery = "INSERT INTO STAFF (ID,
            NAME) Values(1002,"Jane Doe")";
            DB2Command myComm = new DB2Command(myInsertQuery);
            myComm.Connection = myConn;
            myConn.Open();
            myComm.ExecuteNonQuery();
            myConn.Close();
            

我们可以看到,DB2Command——这个特定于 DB2 风格的 DbCommand——可用于直接运行对 DB2 服务器的查询。对于 BDP 也有相应的接口,因此


            BdpConnection myConn = new
            Borland.Data.Provider.BdpConnection(connectionstring);
            string myInsertQuery = "INSERT INTO STAFF (ID,
            NAME) Values(1002,"Jane Doe")";
            BdpCommand myComm = new BdpCommand(myInsertQuery);
            myComm.Connection = myConn;
            myConn.Open();
            myComm.ExecuteNonQuery();
            myConn.Close();
            

可以运行在任何有 BDP 驱动程序的服务器上。

SQL INSERT 命令不能返回结果,但是可以带参数。ADO.NET 可以使用 DbDataParameter 类通过 Parameter 属性来包含参数。同样,参数类也是在特定于服务器的基础上实现的。

下面是一个关于参数的例子,它调用一个 DB2 存储过程以便从数据库返回完整的名称,假设是一个电子邮件地址。首先是存储过程:


            CREATE PROCEDURE Job(
            IN v_Name VARCHAR(50),
            OUT v_Job VARCHAR(50))
            DYNAMIC RESULT SETS 1
            LANGUAGE SQL
            BEGIN
            SELECT Job INTO v_Job FROM Staff WHERE Name = v_Name
            FETCH FIRST 1 ROWS ONLY;
            END
            

然后是 C# 代码片断。我们创建连接,创建一个命令,然后创建两个 BdpParameter 对象并将它们添加到命令对象。


            public String GetJob(String Name)
            {
            BdpConnection myConn = new BdpConnection(<connection string>);
            BdpCommand myCommand = new BdpCommand("Job", myConn);
            // This is a stored procedure - mark the command accordingly
            myCommand.CommandType = CommandType.StoredProcedure;
            // Add the input parameter
            BdpParameter paramName = new BdpParameter("V_NAME", BdpType.String, 50);
            paramName.Direction = ParameterDirection.Input;
            paramName.Precision = 50;
            myCommand.Parameters.Add(paramName);
            // The output parameter
            BdpParameter paramJob = new BdpParameter("V_JOB", BdpType.String, 50);
            paramJob.Direction = ParameterDirection.Output;
            paramJob.Precision = 50;
            myCommand.Parameters.Add(paramJob);
            paramName.Value = Name;
            myConn.Open();
            myCommand.ExecuteNonQuery();
            myConn.Close();
            // The return value is passed as an Object. We need to cast as a string
            return (string)paramJob.Value;
            }

这里有两个重要的地方需要注意。首先,参数声明的顺序应该与存储过程中声明的顺序一致。其次,您需要显式地将参数类型覆盖为服务器所期望的特定的底层数据类型。下面是 DB2 管理的提供者和 BDP 所支持的一些不同的数据类型的例子:

DB2 数据类型 DB2 管理的提供者 Borland Data Provider
VARCHAR DB2Type.VarChar BdpType.String
INT DB2Type.Integer BdpType.Int16, BdpType.Int32, 或 BdpType.Int64, 根据需要而定
IMAL (x, y) DB2Type.Decimal BdpType.Decimal
TIMESTAMP DB2Type.TimeStamp BdpType.DateTime

您还将需要为这些数据类型设置 Size 和 Precision 属性。

这种方法与 BDE and dbExpress 有很大的不同,后者使用 TQuery 和 TSQLQuery 组件来调用存储过程或者其他数据定义查询,这种调用不会返回列表结果集。这些应该用 DbCommand 对象代替。然而,我们可以看到,如果数据需要更新的话,通常 SELECT 查询要么需要使用一个 DataReader,要么需要使用一个 DataSet。

使用 DataReader 直接访问 DbCommand 数据 
我刚才为 DbCommand 类列出的这些例子没有返回一个数据集。DbCommand 可以处理这一点,但是您将需要使用 DataReader 组件来获取数据。

乍一看来,DataReader 好像受到一些限制。您只能向前移动,每次读取一条记录,没有数据缓存。然而,这些限制比起它的主要特性,即速度来就不足道了——DataReader 速度极快。

奇怪的是一个程序频繁地使用适合于 DataReader 的一条 SELECT 查询。其中一个例子就是从一个准备显示在 Web 页面上的表返回一个记录块:


            public String GetStaff(string Job)
            {
            BdpConnection myConn = new
            BdpConnection(<connection string>);
            BdpCommand myCommand = new BdpCommand("SELECT
            NAME, YEARS FROM STAFF WHERE JOB=? FETCH FIRST 10
            ROWS ONLY", myConn);
            BdpParameter paramJob = new BdpParameter("JOB",
            BdpType.String, 50);
            paramJob.Direction = ParameterDirection.Input;
            paramJob.Precision = 50;
            myCommand.Parameters.Add(paramJob);
            paramJob.Value = Job;
            myConn.Open();
            BdpDataReader readerStaff =
            myCommand.ExecuteReader();
            string staffList="";
            while (readerStaff.Read()) {
            staffList += readerStaff.GetString(0)+"
            ("+readerStaff.GetInt16(1)+") <br />";
            }
            readerStaff.Close();
            myConn.Close();
            return staffList;
            }

这个例子使用了 BDP。DB2 管理的提供者的语法非常的相似:只是把 "Bdp? components to be "Db2? 组件和 BdpType.String 改为 DB2Type.VarChar。

这个例子显示了如何使用 SELECT 查询中的参数——这个过程与存储过程中使用的过程很相似。您需要添加 Parameter 对象到 Command.Parameters 属性,添加的顺序与查询中定义 "?" 占位符的顺序一致。注意,我们将显式地告诉系统需要什么样的数据,这样可以使代码运行得更快。

对于 BDE 没有 IDataReader 的同等物:尽管 TQuery 有一个 UniDirectional 属性,但这只能影响 BDE,而对于对数据库的底层查询没有作用,因此这里总是要准备一定程度的缓冲。

在 dbExpress 中最接近的同等物是 TSQLQuery 组件,该组件同样返回一个前向结果集。然而,我们已经看到,使用 ADO.NET 检索结果集更加容易。

DbDataAdapter 和 DataSet
如果您想缓存数据并交互作用地浏览数据时该怎么办呢?这就是 DataSet 对象可以做的。这种强大的对象可以管理一个本地数据缓存,缓存的数据是通过使用一个 DbDataAdapter 对象从一个数据库中检索到的。然后可以使用这个缓存作为其他可视化 .NET Framework 组件(例如 DataGrid 或 TextBox)的数据源。

在缓存数据的时候,DataSet 对象与 VCL TClientDataSet 相似。然而,它要强大得多:例如,它可以存储来自多个表中的数据。也就是说,当我们建立 DataSet 对象时,我们需要告诉这个对象要针对什么表以及要将哪些列存入该对象中。例如:


            // Create a new dataset
            DataSet StaffDataSet = new DataSet("DSStaff");
            // Set up the columns for the table for the dataset
            DataColumn IDCol = new DataColumn("ID", typeof(short));
            DataColumn NameCol = new DataColumn("NAME");
            DataColumn YearsCol = new DataColumn("YEARS", typeof(short));
            // Set up the table for the dataset and add the columns
            DataTable StaffTable = new DataTable("TabStaff");
            StaffTable.Columns.AddRange(new DataColumn[] {IDCol, NameCol, YearsCol});
            // Add the table to the dataset
            StaffDataSet.Tables.AddRange(new DataTable[] {StaffTable});
            

您可以使用很多参数来微调这些对象中每一个对象的行为——参见联机帮助以获得一个概述。我将使用可以将所需代码减至最少的构造函数。注意,DataSet 组件只有一种风格。不管您选择哪个受管的提供者,DataSet 组件都是一样的。

我们的代码只配置 DataSet,但是不检索数据。要检索数据,我们需要一个 DbDataAdapter。这个组件不仅可以从数据库检索数据,还可以从 DataSet 将改过的数据放回数据库。

因为还必须将不同的数据类型映射到 .NET Framework 数据类型,DbDataAdapter 对象因所使用的数据库服务器的不同而采用不同的风格。为了使用 DbDataAdapter 组件,通常需要设置一些属性,以便定义用于检索、更新、插入和删除记录的查询。如果您想更新数据,则可以调用这些查询。

所有这些查询都存储在一个 DbCommand 对象中,因此,如果您想处理更新,需要创建 4 个查询,每一个查询都要参数化——这很难掌握。

不过,如果您只是想读取数据,那么事情就比较简单了。下面的代码片断展示了如何使用 BDP 来做这种工作:


            BdpConnection myConn = new
            BdpConnection(ConnString);
            // Create a command object to retrieve data
            BdpCommand SelComm = new BdpCommand("SELECT ID,
            NAME, YEARS FROM STAFF", myConn);
            // Create the data adapter
            BdpDataAdapter StaffDA = new BdpDataAdapter();
            // Point the data adapter to the command object to
            get data
            StaffDA.SelectCommand = SelComm;
            

首先,我建立连接,然后建立一个 BdpCommand 对象,用以检索数据。DataAdapter 用于将 BdpCommand 链接到 DataSet 中的 DataTable。至此您可能会预想建立 DB2DataAdapter 的过程是类似的:用 DB2 中的同等物替代 BDP 组件。

创建好 DbDataAdapter 之后,您需要填充 DataSet。这里,在 BdpDataAdapter 与其他风格之间会有所不同。对于 DB2DataAdapter 和其他特定于服务器的驱动程序,您需要自己在代码中告诉 DbDataAdapter 填充 DataSet。

例如,下面的代码可以添加到使用 DB2 管理的提供者的一个窗体的 Open 方法中:


            StaffDA.Fill(StaffDataSet);
            this.dataGrid1.DataMember = "TabStaff";
            this.dataGrid1.DataSource = StaffDataSet;
            

BDP 使得事情更容易一点。一个额外的 active 属性可用于告诉 DataAdapter 自动地根据请求填充一个 DataSet 中的一个 DataTable。因此这里不是在 Open 方法中设置属性,而是将以下几行添加到初次建立连接时运行的代码当中:


            // Point the data adapter to the DataSet and the table within it
            StaffDA.DataSet = StaffDataSet;
            StaffDA.DataTable = StaffTable;
            // Tell the DataAdapter to automatically populate the table
            StaffDA.Active = true;
            

这样做的一个很大的副作用是 DataSet 可以在设计的时候填充,从而暴露了活动数据。尽管这种情况只有当 BdpDataAdapter 被与 DataSet 和相关的控件放在同一个窗体上的时候才会发生,但这的确是个陷阱。实际上您可能会发现,对于更大的系统,您将使用一个中央对象来封装 DataSet,以便您可以在多个窗体之间共享它。

如果您以前使用的是 BDE,您可以认为 DataSet 组件与 TClientDataSet 有点相像。它们之间在表面上有些类似之处:与 TClientDataSet 一样,DataSet 将数据缓存在工作站本地,这是一种使用数据的有效方法,因为数据是在一个大块中读取和更新的。与 BDE 一样,当它返回一个只读结果集时,DataAdapter 为开发者提供了 SQL 查询以更新源数据库。

然而,在 TClientDataSet 的背后却颇有不同。与 BDE TClientDataSet 不一样,DataSet 可以缓存多个表。DataSet 在内部管理数据,把数据当作 XML 模式的关系视图,因此它没有“游标”或当前记录的概念。相反,当前记录指针是由数据绑定层管理的。

另一个不同之处就是处理更新时所用的技术。TClientDataSet 保留了对表的更新日志,这种方法使得处理多层的“undo”非常方便。相反,当使用数据绑定控件进行更新时,DataSet 将更新一行接一行地保存,只保留被更新和删除的行的最初版本。

从 BDE 转移到 ADO.NET 和 BDP
如果您习惯使用 BDE,您会希望看看一些组件,下面就是对这些组件的一个简短的总结。这里并没有列出所有的组件,而是很自然地经过了简化,但应该可以帮助您理解从哪开始。

BDE 组件 功能 ADO.NET 组件 功能
TSession 和TDataBase 建立到数据库的连接和管理事务 DB2Connection, BdpConnection 建立到数据库的连接和管理事务
Ttable 管理一个表的结果集的本地缓存 没有直接的同等物。要么同时使用 DataSet 和 DbDataAdapter,要么同时使用 DbCommand 和 DataReader DataSet 根据查询创建一个或多个表的本地缓存
Tquery 管理一个查询的本地缓存 没有直接的同等物。要么使用 DataSet / DbDataAdapter,要么使用 DbCommand / DataReader DbCommand / DataReader 管理查询结果的前向游标
TstoredProc 执行一个存储过程 DbCommand 不能执行存储过程,可以可选地返回一个结果集
TdataSource 充当 dataset 与数据感知控件(data aware control)之间的通道 没有直接的同等物 数据感知控件链接到 DataSet 对象中的列
TDBText, TDBEdit, etc. Windows 控件的数据感知版本 使用常规的 Windows Forms 所有适当的 Windows Forms 都是数据感知的

结束语
ADO.NET 是一个丰富的、先进的数据库访问库。虽然 ADO.NET 与 Borland Database Engine 有很大的不同,但是 Borland Data Provider 经过改进的特性使得它更易于使用。BDP 为需要访问 DB2 的 .NET 开发人员提供了一种高效的、可伸缩的创建 Windows 客户机和 Web 应用程序的方法。

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