(转)使用 ODP.NET 和引用游标优化结果集

利用引用游标和 ODP.NET 的强大功能创建强大、灵活和可伸缩的应用程序。

本文的相关下载
示例代码
Oracle Data Provider for .NET (ODP.NET)
Oracle 数据库 10g

查看完整的“精通 Oracle 的 .NET 应用程序开发”目录

利用 Oracle Data Provider for .NET,可以通过多种方法将 Oracle 数据库中的查询结果返回给客户端应用程序;最强大、最灵活、最具伸缩性的方法之一是使用引用游标。 本文介绍了引用游标及它的应用,其中包括一个将多个活动结果集用于 Oracle 的示例。 此示例代码演示了在 .NET 代码中使用引用游标的极其简单的过程。 如果您刚刚开始使用 Oracle Data Provider for .NET,请参阅 John Paul Cook 的文章“基于 Oracle 数据库构建 .NET 应用程序”,了解结合使用提供程序与 Visual Studio .NET 开发环境的过程。

前提条件

  • Oracle9i 数据库或 Oracle 数据库 10g 的访问权限
  • HR 示例用户的访问权限
  • Oracle Client 9i 第 2 版或更高版本
  • Oracle Data Provider for .NET
  • Microsoft Visual Studio .NET 2002 或更高版本

什么是引用游标?

如果您并不熟悉引用游标,那么您提出的第一个问题自然就是“到底什么是引用游标?” 简单而言,引用游标是一个 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 :

  • 引用游标引用服务器内存。 引用游标表示的内存地址“驻留”在数据库服务器上,而非客户端计算机上。 这意味着客户端必须在引用游标的生命周期期间保持数据库连接。 如果基础数据库连接关闭,将无法从客户端访问引用游标。
  • 引用游标涉及额外的数据库往返。由于引用游标是指向服务器上的内存的指针,该指针要返回至客户端,而引用游标中包含的实际数据最初并不返回至客户端。 例如,当客户端通过调用 OracleCommand 对象的 ExecuteNonQuery 方法打开引用游标时,将只返回数据在服务器上的内存地址。 客户端必有在打开引用游标后请求引用游标中包含的数据。 尽管它需要额外的往返,但这在某些情况下对性能是很有好处的。 在用户尝试读取数据之前将不检索数据,从而可以防止从存储过程返回许多查询结果时可能出现的瓶颈。
  • 引用游标无法更新。引用游标表示的结果集是只读的。 无法通过引用游标更新数据库。
  • 引用游标无法向后滚动。只能以向前的、顺次的方式访问引用游标表示的数据。 不能将记录指针置于引用游标的内部,以指向结果集中的随机记录。
  • 引用游标是一个 PL/SQL 数据类型。 请在 PL/SQL 代码块内部创建和返回引用游标。
  • 引用游标可以是弱类型或强类型的。强类型的引用游标包含一个返回类型,该返回类型是在 PL/SQL 中声明引用游标本身时定义的。 与强类型的引用游标相比,弱类型的引用游标没有定义返回类型,这意味着弱类型的引用游标可以引用任何类型的查询工作区。 而强类型的引用游标只能引用特定类型的查询工作区,即该工作区的类型(或结构)必须与声明引用游标时使用的类型(或结构)相同。 换言之,强类型的引用游标是特定的,而弱类型的引用游标是一般的。 本文使用弱类型的引用游标。

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 程序包是一个用于将相关项目存储或捆绑为逻辑实体的机制。 程序包由两个不同的部分组成:

  • 程序包规范。定义程序包中包含的内容,类似某种语言(如 C++)中的头文件。 规范中定义的项目是公共的。 即,规范和主体外部的代码可以“看到”这些项目。 规范是已发布的程序包接口。
  • 程序包主体。包含规范中定义的过程和函数的代码。 主体可能还包含规范中未声明的代码;这种情况下,此代码是专用的并且只对程序包主体内的代码可见。

这两个部分在数据字典中作为单独对象分开存储,并可以在 user_source 视图中看到。 规范存储为 PACKAGE 类型,主体存储为 PACKAGE BODY 类型。 注意,可以拥有一个无主体的规范。 但不能有一个无规范的主体。 例如,可以使用无主体的规范声明一组公共常量;由于一组常量不需要实现,因此主体不是必要的。 可以通过使用用于 Visual Studio .NET 的 Oracle 开发人员工具查看程序包主体和规范。

示例应用程序

既然您已经牢固掌握了在 .NET 应用程序中使用引用游标时所涉及的各种概念和结构,现在我们将了解一些代码以创建一个控制台应用程序示例,用于演示将各个部分组合在一起的过程。 由于您要使用客户端应用程序中的 OracleDataReaderDataSet 类的实例从引用游标检索数据,因此可以很方便对此示例加以更改,以作为传统 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”类方法,以将结果显示到控制台窗口:

  • DisplayRefCursorData 方法: 此方法是一个的重载方法,用来显示 DataSet 对象或 OracleDataReader 对象中的所有数据。
  • DisplayDataReaderRow 方法: 此方法用于显示 OracleDataReader 对象中的一行数据。
  • GetCursorFunction 方法: 此方法用于将引用游标作为函数返回值进行检索。
  • GetCursorParameter 方法: 此方法用于将引用游标作为过程中的输出参数进行检索。
  • TraverseResultSets 方法: 此方法用于检索多个游标并演示了如何顺次遍历每个结果集。
  • MultipleActiveResultSets 方法: 此方法用于检索和“随机”处理多个活动的结果集。

Main 方法

示例代码中的 Main 方法的用途是建立一个数据库连接,然后调用每个成员方法以演示如何使用引用游标。注意: 要运行此示例,请确保修改连接字符串中的User Id、PasswordData 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、OracleDataAdapterDataSet 类结合使用)可以轻松地用于将服务器中的数据读取到 .NET 代码中,你还从完整地完成了一个示例控制台应用程序,该程序包含了这些不同的内容。 完成本文中的步骤之后,您应该能够在 .NET 应用程序中实现引用游标。

魔兽就是毒瘤,大家千万不要玩。
原文地址:https://www.cnblogs.com/tracy/p/1851281.html