精通使用 Oracle 的 .NET 应用程序开发
利用引用游标和 ODP.NET 的强大功能创建强大、灵活和可伸缩的应用程序。
利用 Oracle Data Provider for .NET,可以通过多种方法将 Oracle 数据库中的查询结果返回给客户端应用程序;最强大、最灵活、最具伸缩性的方法之一是使用引用游标。 本文介绍了引用游标及它的应用,其中包括一个将多个活动结果集用于 Oracle 的示例。 此示例代码演示了在 .NET 代码中使用引用游标的极其简单的过程。 如果您刚刚开始使用 Oracle Data Provider for .NET,请参阅 John Paul Cook 的文章“基于 Oracle 数据库构建 .NET 应用程序”,了解结合使用提供程序与 Visual Studio .NET 开发环境的过程。 前提条件
什么是引用游标? 如果您并不熟悉引用游标,那么您提出的第一个问题自然就是“到底什么是引用游标?” 简单而言,引用游标是一个 PL/SQL 数据类型,它的值为一个地址值,用于表示查询工作区在数据库服务器上的内存位置。 您接下来可能又想要知道什么是查询区。 可以将查询工作区看作是服务器上的结果集(有时称作行集)- 它是查询结果在服务器内存中的存储位置。 当您开始看到“查询工作区”和“内存地址”这样的术语时,您可能开始认为引用游标比较复杂并需要处理 C 样式指针等对象。 幸运地是,Oracle Data Provider for .NET 并不存在这样的情况。 实际上,在使用 Oracle Data Provider for .NET 时,可以将引用游标只看作是服务器上的结果集的句柄。 由于引用游标是一个 PL/SQL 数据类型,因此需要通过某种方法在 .NET 代码中表示引用游标,可以通过 Oracle Data Provider for .NET 公开的 OracleRefCursor 类完成此操作。 引用游标的特性 引用游标有几个重要特性必须要考虑到,只有这样才能在代码中正确地结合使用引用游标与 Oracle Data Provider for .NET :
OracleRefCursor 类 前面已经进行过简单介绍,在 .NET 代码中表示引用光标是通过 OracleRefCursor 类实现的,该类由 Oracle Data Provider for .NET 在 Oracle.DataAccess.Types 命名空间中公开。 OracleRefCursor 类是一个简单类,该类没有构造函数,它将 GetDataReader 方法公开为一种数据访问方法,用于访问服务器上的查询工作区中存储的数据。 还可以将 OracleRefCursor 类与 OracleDataAdapter 类结合使用以填充 DataTables 和 DataSets。 由于 OracleRefCursor 类没有构造函数,因此不要采用标准方法来完成此类对象的实例化。 而是创建 OracleParameter 类的实例并将 OracleDbType 属性设置为值 RefCursor 。请注意,引用游标是一个 PL/SQL 数据类型,因此必须使用 OracleParameter 类的实例将引用光标作为参数传出 PL/SQL 块。 此外,还可以将引用游标作为输出参数或函数返回值传递给调用客户端。 在 Oracle 数据库 10g 第 2 版的 ODP.NET 中,可以将引用光标作为输入参数传递。 为什么使用 PL/SQL 和引用游标? 此时,您已经知道什么是引用游标,了解了引用游标的某些重要属性,并知道需要将 OracleRefCursor 类与 OracleParameter 类结合使用以将 PL/SQL 中的引用游标传递到 .NET 代码中。 人们通常问我的两个相关问题是“不能只将 SQL 语句嵌入到代码中并使用 OracleCommand 从数据库中获取数据吗?”和“如果可以的话,为什么还要使用 PL/SQL 和引用游标?” 我对第一个问题的回答是“是的,可以”。我对第二个问题的回答是“根据情况的不同,这样做是有意义的”。 下面我将进行解释。 首先,可以创建整个应用程序而不必编写或调用任何 PL/SQL 代码行。 而引用游标有助于优化 Oracle 数据检索。 使用 PL/SQL 的主要好处之一是它与 Oracle 数据库和 SQL 语言紧密集成在一起。 例如,表中的 Oracle 列类型通常是 PL/SQL 数据类型,反之亦然。 这样,您只用处理一个变量,该变量既是数据库中的表的正确数据类型,也是所使用的编程语言的正确数据类型。 另一个有时忽略的好处是您可以将 PL/SQL 用作安全工具或机制。 可以创建一个 PL/SQL 过程,用于返回用户无法直接访问(他们只能通过 PL/SQL 代码访问数据)的数据库表中的数据。 用户需要有 PL/SQL 过程或函数的相应执行权限才能顺利的使用此方案,数据库管理员负责授予这些权限。 此外,通过使用 PL/SQL,您已经将处理数据的代码移动到数据库中,并完成了客户端逻辑与数据逻辑的分离。 将代码移动到数据库中即是完成了代码的集中,因此只需要在一个位置管理它。 引用游标的主要用途是使 PL/SQL 代码能够将结果集返回给用其他语言编写并位于数据库外部的程序。 它是 PL/SQL 代码将结果集返回至客户端应用程序所采用的机制。 如果要将 PL/SQL 代码中的结果集返回至客户端,可以使用引用游标完成此操作。 尽管有很多的原因促使我们采用 PL/SQL , .NET 程序员最初可能会觉得将代码移出 .NET 环境并移入数据库有些不自然。 但请注意,PL/SQL 是为处理 Oracle 数据库中的数据而创建的,并且它的效果很好。 实际上,最新的数据库版本(尤其是 Oracle 数据库 10g)引入了 PL/SQL 编译器和优化器的增强功能,因此从纯性能的角度来看,PL/SQL 是非常吸引人的。 PL/SQL 程序包和程序包体 如果您刚接触 Oracle,则可能对 PL/SQL 程序包和程序包体不太熟悉。 程序包 是 PL/SQL 从 Ada(PL/SQL 所基于的语言)继承来的结构。 简单地说,PL/SQL 程序包是一个用于将相关项目存储或捆绑为逻辑实体的机制。 程序包由两个不同的部分组成:
这两个部分在数据字典中作为单独对象分开存储,并可以在 user_source 视图中看到。 规范存储为 PACKAGE 类型,主体存储为 PACKAGE BODY 类型。 注意,可以拥有一个无主体的规范。 但不能有一个无规范的主体。 例如,可以使用无主体的规范声明一组公共常量;由于一组常量不需要实现,因此主体不是必要的。 可以通过使用用于 Visual Studio .NET 的 Oracle 开发人员工具 查看程序包主体和规范。 示例应用程序 既然您已经牢固掌握了在 .NET 应用程序中使用引用游标时所涉及的各种概念和结构,现在我们将了解一些代码以创建一个控制台应用程序示例,用于演示将各个部分组合在一起的过程。 由于您要使用客户端应用程序中的 OracleDataReader 和 DataSet 类的实例从引用游标检索数据,因此可以很方便对此示例加以更改,以作为传统 Windows 客户端或 ASP.NET 客户端工作。 您将使用 HR 示例模式,它是 Oracle9i 和 Oracle 10g 软件附带的示例模式之一。 此示例应用程序将检索 HR 模式中的 EMPLOYEES 表中的列和行的子集,并将结果显示在控制台窗口中。 您需要使用过程的函数返回值和输出参数。 PL/SQL 代码 您可以先创建 PL/SQL 代码或 .NET 代码;但由于您将需要知道要在 .NET 代码中使用的程序包、函数和过程的名称,因此先创建 PL/SQL 代码更合乎逻辑。 要创建 PL/SQL 程序包和程序包主体,请以 HR 用户的身份使用 SQL*Plus 登录到数据库。 注意,HR 用户在默认情况下处于锁定状态。 在登录到数据库之前,您必须解除帐户锁定。 成功登录到数据库后,请执行 otn_ref_cursor.sql 脚本以创建 PL/SQL 程序包和程序包主体。 此脚本包含在示例代码下载文件中。 下载文件中所包含的 README.txt 文件提供了执行此脚本的详细信息。 以下是 otn_ref_cursor.sql 文件:
create or replace package otn_ref_cursor as -- used to illustrate passing a ref cursor -- as a return value from a function -- or as an output parameter from a procedure function get_emp_info return sys_refcursor; procedure get_emp_info(p_rc out sys_refcursor); procedure get_multiple_cursors(p_rc1 out sys_refcursor, p_rc2 out sys_refcursor, p_rc3 out sys_refcursor); end; /
现在,您已经创建了 PL/SQL 程序包,下面便可以创建 PL/SQL 程序包主体。 以下用于创建 PL/SQL 程序包主体的代码也是 otn_ref_cursor.sql 文件中的一部分:
create or replace package body otn_ref_cursor as function get_emp_info return sys_refcursor is -- declare the cursor variable -- sys_refcursor is a built in type l_cursor sys_refcursor; begin open l_cursor for select employee_id, last_name, first_name, to_char(hire_date, 'DD-MON-YYYY') hire_date from employees where last_name like 'A%' order by last_name, first_name; return l_cursor; end; procedure get_emp_info(p_rc out sys_refcursor) is begin -- open the cursor using the passed in ref cursor -- sys_refcursor is a built in type open p_rc for select employee_id, last_name, first_name, to_char(hire_date, 'DD-MON-YYYY') hire_date from employees where last_name like 'A%' order by last_name, first_name; end; procedure get_multiple_cursors(p_rc1 out sys_refcursor, p_rc2 out sys_refcursor, p_rc3 out sys_refcursor) is begin -- open the cursors using the passed in ref cursor parameters -- sys_refcursor is a built in type open p_rc1 for select employee_id, last_name, first_name, to_char(hire_date, 'DD-MON-YYYY') hire_date from employees where last_name like 'A%' order by last_name, first_name; open p_rc2 for select employee_id, last_name, first_name, to_char(hire_date, 'DD-MON-YYYY') hire_date from employees where last_name like 'B%' order by last_name, first_name; open p_rc3 for select employee_id, last_name, first_name, to_char(hire_date, 'DD-MON-YYYY') hire_date from employees where last_name like 'C%' order by last_name, first_name; end; end; /
类方法 在创建类方法的过程中,我们展示了不同引用游标使用方法。 此外,您还将创建“helper”类方法,以将结果显示到控制台窗口:
Main 方法 示例代码中的 Main 方法的用途是建立一个数据库连接,然后调用每个成员方法以演示如何使用引用游标。注意: 要运行此示例,请确保修改连接字符串中的 User Id、Password 和 Data Source 参数(如果它们不同于下面的参数)。
// C# static void Main(string[] args) { // create a connection to the database // change values as needed for your environment OracleConnection con = new OracleConnection("User Id=hr; Password=hr; Data Source=otndemo; Pooling=false"); // attempt to open the connection try { con.Open(); } catch (OracleException ex) { Console.WriteLine(ex.Message); } // only call our methods if we are connected // to the database if (con.State == ConnectionState.Open) { // call method that gets a ref cursor from pl/sql function GetCursorFunction(con); // call method that gets a ref cursor from pl/sql procedure GetCursorParameter(con); // call method that serially traverses multiple result sets TraverseResultSets(con); // call method that illustrates multiple active result sets (MARS) MultipleActiveResultSets(con); } // clean up the connection object con.Dispose(); } ' Visual Basic .NET Sub Main() ' create a connection to the database ' change values as needed for your environment Dim con As OracleConnection = New OracleConnection ("User Id=hr;Password=hr;Data Source=otndemo;Pooling=false") ' attempt to open the connection Try con.Open() Catch ex As OracleException Console.WriteLine(ex.Message) End Try ' only call our methods if we are connected ' to the database If con.State = ConnectionState.Open Then ' call method that gets a ref cursor from pl/sql function GetCursorFunction(con) ' call method that gets a ref cursor from pl/sql procedure GetCursorParameter(con) ' call method that serially traverses multiple result sets TraverseResultSets(con) ' call method that illustrates multiple active result sets (MARS) MultipleActiveResultSets(con) End If con.Dispose() End Sub
用于演示如何使用引用游标的各种方法全部遵循同一模式。 每个方法都创建一个用于调用数据库中的 PL/SQL 代码的 OracleCommand 对象。 OracleCommand 对象的 CommandType 属性设置为值 CommandType.StoredProcedure ,以指示命令文本表示数据库中存储的 PL/SQL 代码的名称。 OracleCommand 对象的命令文本和数据库连接在 OracleCommand 对象构造函数调用中初始化。 创建 OracleCommand 对象后,将创建一个 OracleParameter 对象。 该对象将表示 .NET 代码中的引用游标。 可以通过将每个参数对象的 OracleDbType 属性设置为 OracleDbType.RefCursor 来完成此任务。 使用引用游标时必须要正确设置此属性。 根据您是调用前面创建的 PL/SQL 程序包中的函数还是过程,适当设置 ParameterDirection 属性值。 此参数随后将添加到命令对象集合中,并执行。 随后使用 OracleDataAdapter 访问引用光标,可将其当做 OracleDataReader 对象或 DataSet 进行访问。 最后,处理对象以释放资源。 GetCursorFunction 方法代码调用 PL/SQL 程序包中的 get_emp_info 函数。 使用 PL/SQL 函数时,正确声明 ParameterDirection(如本文中的示例所示)是很重要的。 此代码如下:
// C# static void GetCursorFunction(OracleConnection con) { // display a simple marker line to the console // to indicate where we are Console.WriteLine("In GetCursorFunction..."); Console.WriteLine(); // create the command object and set attributes OracleCommand cmd = new OracleCommand("otn_ref_cursor.get_emp_info", con); cmd.CommandType = CommandType.StoredProcedure; // create parameter object for the cursor OracleParameter p_refcursor = new OracleParameter(); // this is vital to set when using ref cursors p_refcursor.OracleDbType = OracleDbType.RefCursor; // this is a function return value so we must indicate that fact p_refcursor.Direction = ParameterDirection.ReturnValue; // add the parameter to the collection cmd.Parameters.Add(p_refcursor); // create a data adapter to use with the data set OracleDataAdapter da = new OracleDataAdapter(cmd); // create the data set DataSet ds = new DataSet(); // fill the data set da.Fill(ds); // display the data to the console window DisplayRefCursorData(ds); // clean up our objects release resources ds.Dispose(); da.Dispose(); p_refcursor.Dispose(); cmd.Dispose(); Console.WriteLine(); } ' Visual Basic .NET Sub GetCursorFunction(ByVal con As OracleConnection) ' display a simple marker line to the console ' to indicate where we are Console.WriteLine("In GetCursorFunction...") Console.WriteLine() ' create the command object and set attributes Dim cmd As OracleCommand = New OracleCommand("otn_ref_cursor.get_emp_info", con) cmd.CommandType = CommandType.StoredProcedure ' create parameter object for the cursor Dim p_refcursor As OracleParameter = New OracleParameter ' this is vital to set when using ref cursors p_refcursor.OracleDbType = OracleDbType.RefCursor ' this is a function return value so we must indicate that fact p_refcursor.Direction = ParameterDirection.ReturnValue ' add the parameter to the collection cmd.Parameters.Add(p_refcursor) ' create a data adapter to use with the data set Dim da As OracleDataAdapter = New OracleDataAdapter(cmd) ' create the data set Dim ds As DataSet = New DataSet ' fill the data set da.Fill(ds) ' display the data to the console window DisplayRefCursorData(ds) ' clean up our objects release resources ds.Dispose() da.Dispose() p_refcursor.Dispose() cmd.Dispose() Console.WriteLine() End Sub
GetCursorParameter 方法代码调用 PL/SQL 程序包中的 get_emp_info 过程。 使用 PL/SQL 函数时便存在这种情况,使用过程中的输出参数时正确声明 ParameterDirection 是很重要的。 此代码如下:
// C# static void GetCursorParameter(OracleConnection con) { // display a simple marker line to the console // to indicate where we are Console.WriteLine("In GetCursorParameter..."); Console.WriteLine(); // create the command object and set attributes OracleCommand cmd = new OracleCommand("otn_ref_cursor.get_emp_info", con); cmd.CommandType = CommandType.StoredProcedure; // create parameter object for the cursor OracleParameter p_refcursor = new OracleParameter(); // this is vital to set when using ref cursors p_refcursor.OracleDbType = OracleDbType.RefCursor; // this is an output parameter so we must indicate that fact p_refcursor.Direction = ParameterDirection.Output; // add the parameter to the collection cmd.Parameters.Add(p_refcursor); // create a data adapter to use with the data set OracleDataAdapter da = new OracleDataAdapter(cmd); // create the data set DataSet ds = new DataSet(); // fill the data set da.Fill(ds); // display the data to the console window DisplayRefCursorData(ds); // clean up our objects release resources ds.Dispose(); da.Dispose(); p_refcursor.Dispose(); cmd.Dispose(); Console.WriteLine(); } ' Visual Basic .NET Sub GetCursorParameter(ByVal con As OracleConnection) ' display a simple marker line to the console ' to indicate where we are Console.WriteLine("In GetCursorParameter...") Console.WriteLine() ' create the command object and set attributes Dim cmd As OracleCommand = New OracleCommand("otn_ref_cursor.get_emp_info", con) cmd.CommandType = CommandType.StoredProcedure ' create parameter object for the cursor Dim p_refcursor As OracleParameter = New OracleParameter ' this is vital to set when using ref cursors p_refcursor.OracleDbType = OracleDbType.RefCursor ' this is an output parameter so we must indicate that fact p_refcursor.Direction = ParameterDirection.Output ' add the parameter to the collection cmd.Parameters.Add(p_refcursor) ' create a data adapter to use with the data set Dim da As OracleDataAdapter = New OracleDataAdapter(cmd) ' create the data set Dim ds As DataSet = New DataSet ' fill the data set da.Fill(ds) ' display the data to the console window DisplayRefCursorData(ds) ' clean up our objects release resources ds.Dispose() da.Dispose() p_refcursor.Dispose() cmd.Dispose() Console.WriteLine() End Sub
TraverseResultSets 方法代码在单个数据库调用中检索多个游标并演示了利用 Oracle Data Provider for .NET 实现顺次访问结果集的过程。
// C# static void TraverseResultSets(OracleConnection con) { // display a simple marker line to the console // to indicate where we are Console.WriteLine("In TraverseResultSets..."); Console.WriteLine(); // create the command object and set attributes OracleCommand cmd = new OracleCommand("otn_ref_cursor.get_multiple_cursors", con); cmd.CommandType = CommandType.StoredProcedure; // create parameter objects for the cursors OracleParameter p_rc1 = new OracleParameter(); OracleParameter p_rc2 = new OracleParameter(); OracleParameter p_rc3 = new OracleParameter(); // this is vital to set when using ref cursors p_rc1.OracleDbType = OracleDbType.RefCursor; p_rc2.OracleDbType = OracleDbType.RefCursor; p_rc3.OracleDbType = OracleDbType.RefCursor; // these are output parameters so we must indicate that fact p_rc1.Direction = ParameterDirection.Output; p_rc2.Direction = ParameterDirection.Output; p_rc3.Direction = ParameterDirection.Output; // add the parameters to the collection cmd.Parameters.Add(p_rc1); cmd.Parameters.Add(p_rc2); cmd.Parameters.Add(p_rc3); // work with an OracleDataReader rather // than a DataSet to illustrate ODP.NET features OracleDataReader dr = cmd.ExecuteReader(); // display the data in the first ref cursor Console.WriteLine("Displaying ref cursor #1:"); DisplayRefCursorData(dr); Console.WriteLine(); // the Oracle Data Provider follows the standard // by exposing the NextResult method to traverse // multiple result sets // display the data in the second ref cursor if (dr.NextResult()) { Console.WriteLine("Displaying ref cursor #2:"); DisplayRefCursorData(dr); Console.WriteLine(); } // display the data in the third ref cursor if (dr.NextResult()) { Console.WriteLine("Displaying ref cursor #3:"); DisplayRefCursorData(dr); Console.WriteLine(); } // clean up our objects and release resources dr.Dispose(); p_rc1.Dispose(); p_rc2.Dispose(); p_rc3.Dispose(); cmd.Dispose(); } ' Visual Basic .NET Sub TraverseResultSets(ByVal con As OracleConnection) ' display a simple marker line to the console ' to indicate where we are Console.WriteLine("In TraverseResultSets...") Console.WriteLine() ' create the command object and set attributes Dim cmd As OracleCommand = New OracleCommand("otn_ref_cursor.get_multiple_cursors", con) cmd.CommandType = CommandType.StoredProcedure ' create parameter objects for the cursors Dim p_rc1 As OracleParameter = New OracleParameter Dim p_rc2 As OracleParameter = New OracleParameter Dim p_rc3 As OracleParameter = New OracleParameter ' this is vital to set when using ref cursors p_rc1.OracleDbType = OracleDbType.RefCursor p_rc2.OracleDbType = OracleDbType.RefCursor p_rc3.OracleDbType = OracleDbType.RefCursor ' these are output parameters so we must indicate that fact p_rc1.Direction = ParameterDirection.Output p_rc2.Direction = ParameterDirection.Output p_rc3.Direction = ParameterDirection.Output ' add the parameters to the collection cmd.Parameters.Add(p_rc1) cmd.Parameters.Add(p_rc2) cmd.Parameters.Add(p_rc3) ' work with an OracleDataReader rather ' than a DataSet to illustrate ODP.NET features Dim dr As OracleDataReader = cmd.ExecuteReader() ' display the data in the first ref cursor Console.WriteLine("Displaying ref cursor #1:") DisplayRefCursorData(dr) Console.WriteLine() ' the Oracle Data Provider follows the standard ' by exposing the NextResult method to traverse ' multiple result sets ' display the data in the second ref cursor If (dr.NextResult()) Then Console.WriteLine("Displaying ref cursor #2:") DisplayRefCursorData(dr) Console.WriteLine() End If ' display the data in the third ref cursor If (dr.NextResult()) Then Console.WriteLine("Displaying ref cursor #3:") DisplayRefCursorData(dr) Console.WriteLine() End If ' clean up our objects and release resources dr.Dispose() p_rc1.Dispose() p_rc2.Dispose() p_rc3.Dispose() cmd.Dispose() End Sub
MultipleActiveResultSets 方法检索和“随机”处理同时处于活动状态的多个结果集,演示了 Oracle Data Provider for .NET 的特性。 在第一版的 ODP.NET 中就已经推出了此特性。此外,请注意,此代码在处理过程中“跳过”第二个引用游标。 将第一个和第三个结果集检索至 .NET 客户端,但推迟了对第二个结果集的数据检索并一直保存在数据库服务器上。 由于第二个结果集的数据检索可以一直延迟到需要的时候进行,因此可缩短响应时间。
// C# static void MultipleActiveResultSets(OracleConnection con) { // display a simple marker line to the console // to indicate where we are Console.WriteLine("In MultipleActiveResultSets..."); Console.WriteLine(); // create the command object and set attributes OracleCommand cmd = new OracleCommand("otn_ref_cursor.get_multiple_cursors", con); cmd.CommandType = CommandType.StoredProcedure; // create parameter objects for the cursors OracleParameter p_rc1 = new OracleParameter(); OracleParameter p_rc2 = new OracleParameter(); OracleParameter p_rc3 = new OracleParameter(); // this is vital to set when using ref cursors p_rc1.OracleDbType = OracleDbType.RefCursor; p_rc2.OracleDbType = OracleDbType.RefCursor; p_rc3.OracleDbType = OracleDbType.RefCursor; // these are output parameters so we must indicate that fact p_rc1.Direction = ParameterDirection.Output; p_rc2.Direction = ParameterDirection.Output; p_rc3.Direction = ParameterDirection.Output; // add the parameters to the collection cmd.Parameters.Add(p_rc1); cmd.Parameters.Add(p_rc2); cmd.Parameters.Add(p_rc3); // execute the command to open the ref cursors cmd.ExecuteNonQuery(); // work with an OracleDataReader rather // than a DataSet to illustrate ODP.NET features OracleDataReader dr1 = ((OracleRefCursor) p_rc1.Value).GetDataReader(); // notice we are skipping the second (or "middle") ref cursor OracleDataReader dr3 = ((OracleRefCursor) p_rc3.Value).GetDataReader(); // illustrate the multiple result sets are active // by "randomly" displaying data from each one if (dr1.Read()) { Console.WriteLine("Displaying data from ref cursor #1:"); DisplayDataReaderRow(dr1); Console.WriteLine(); } if (dr3.Read()) { Console.WriteLine("Displaying data from ref cursor #3:"); DisplayDataReaderRow(dr3); Console.WriteLine(); } if (dr1.Read()) { Console.WriteLine("Displaying data from ref cursor #1:"); DisplayDataReaderRow(dr1); Console.WriteLine(); } if (dr3.Read()) { Console.WriteLine("Displaying data from ref cursor #3:"); DisplayDataReaderRow(dr3); Console.WriteLine(); } // clean up our objects and release resources dr1.Dispose(); dr3.Dispose(); p_rc1.Dispose(); p_rc2.Dispose(); p_rc3.Dispose(); cmd.Dispose(); } ' Visual Basic .NET Sub MultipleActiveResultSets(ByVal con As OracleConnection) ' display a simple marker line to the console ' to indicate where we are Console.WriteLine("In MultipleActiveResultSets...") Console.WriteLine() ' create the command object and set attributes Dim cmd As OracleCommand = New OracleCommand("otn_ref_cursor.get_multiple_cursors", con) cmd.CommandType = CommandType.StoredProcedure ' create parameter objects for the cursors Dim p_rc1 As OracleParameter = New OracleParameter Dim p_rc2 As OracleParameter = New OracleParameter Dim p_rc3 As OracleParameter = New OracleParameter ' this is vital to set when using ref cursors p_rc1.OracleDbType = OracleDbType.RefCursor p_rc2.OracleDbType = OracleDbType.RefCursor p_rc3.OracleDbType = OracleDbType.RefCursor ' these are output parameters so we must indicate that fact p_rc1.Direction = ParameterDirection.Output p_rc2.Direction = ParameterDirection.Output p_rc3.Direction = ParameterDirection.Output ' add the parameters to the collection cmd.Parameters.Add(p_rc1) cmd.Parameters.Add(p_rc2) cmd.Parameters.Add(p_rc3) ' execute the command to open the ref cursors cmd.ExecuteNonQuery() ' work with an OracleDataReader rather ' than a DataSet to illustrate ODP.NET features Dim dr1 As OracleDataReader = DirectCast(p_rc1.Value, OracleRefCursor).GetDataReader() ' notice we are skipping the second (or "middle") ref cursor Dim dr3 As OracleDataReader = DirectCast(p_rc3.Value, OracleRefCursor).GetDataReader() ' illustrate the multiple result sets are active ' by "randomly" displaying data from each one If (dr1.Read()) Then Console.WriteLine("Displaying data from ref cursor #1:") DisplayDataReaderRow(dr1) Console.WriteLine() End If If (dr3.Read()) Then Console.WriteLine("Displaying data from ref cursor #3:") DisplayDataReaderRow(dr3) Console.WriteLine() End If If (dr1.Read()) Then Console.WriteLine("Displaying data from ref cursor #1:") DisplayDataReaderRow(dr1) Console.WriteLine() End If If (dr3.Read()) Then Console.WriteLine("Displaying data from ref cursor #3:") DisplayDataReaderRow(dr3) Console.WriteLine() End If ' clean up our objects and release resources dr1.Dispose() dr3.Dispose() p_rc1.Dispose() p_rc2.Dispose() p_rc3.Dispose() cmd.Dispose() End Sub
运行示例应用程序 现在您已经创建了所有必需的组件和代码,下面便可以运行此示例并在控制台窗口中查看它的输出。 以下是输出的内容:
In GetCursorFunction... 174, Abel, Ellen, 11-MAY-1996 166, Ande, Sundar, 24-MAR-2000 130, Atkinson, Mozhe, 30-OCT-1997 105, Austin, David, 25-JUN-1997 In GetCursorParameter... 174, Abel, Ellen, 11-MAY-1996 166, Ande, Sundar, 24-MAR-2000 130, Atkinson, Mozhe, 30-OCT-1997 105, Austin, David, 25-JUN-1997 In TraverseResultSets... Displaying ref cursor #1: 174, Abel, Ellen, 11-MAY-1996 166, Ande, Sundar, 24-MAR-2000 130, Atkinson, Mozhe, 30-OCT-1997 105, Austin, David, 25-JUN-1997 Displaying ref cursor #2: 204, Baer, Hermann, 07-JUN-1994 116, Baida, Shelli, 24-DEC-1997 167, Banda, Amit, 21-APR-2000 172, Bates, Elizabeth, 24-MAR-1999 192, Bell, Sarah, 04-FEB-1996 151, Bernstein, David, 24-MAR-1997 129, Bissot, Laura, 20-AUG-1997 169, Bloom, Harrison, 23-MAR-1998 185, Bull, Alexis, 20-FEB-1997 Displaying ref cursor #3: 187, Cabrio, Anthony, 07-FEB-1999 148, Cambrault, Gerald, 15-OCT-1999 154, Cambrault, Nanette, 09-DEC-1998 110, Chen, John, 28-SEP-1997 188, Chung, Kelly, 14-JUN-1997 119, Colmenares, Karen, 10-AUG-1999 In MultipleActiveResultSets... Displaying data from ref cursor #1: 174, Abel, Ellen, 11-MAY-1996 Displaying data from ref cursor #3: 187, Cabrio, Anthony, 07-FEB-1999 Displaying data from ref cursor #1: 166, Ande, Sundar, 24-MAR-2000 Displaying data from ref cursor #3: 148, Cambrault, Gerald, 15-OCT-1999
总结 本文介绍了如何在 .NET 应用程序中使用引用游标, 并介绍了游标的概念以及值得注意的某些重要的引用游标特性。 通过本文,您已经了解到 OracleRefCursor 类(与 OracleParameter、OracleDataReader、OracleDataAdapter 和 DataSet 类结合使用)可以轻松地用于将服务器中的数据读取到 .NET 代码中,你还从完整地完成了一个示例控制台应用程序,该程序包含了这些不同的内容。 完成本文中的步骤之后,您应该能够在 .NET 应用程序中实现引用游标。 Mark A. Williams 是《.NET 专家 Oracle 编程》(Apress,2004 年 11 月)的作者,目前在医疗诊断行业担任 Production DBA。 自从 7.0.1.16 版数据库推出以来,他便一直使用 Oracle 产品,他是一位 Oracle ACE,是 7、8、8i、9i 和 10g 版本数据库的 Oracle 认证数据库管理员专家。 |