zoukankan      html  css  js  c++  java
  • (转)使用 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 应用程序中实现引用游标。

    魔兽就是毒瘤,大家千万不要玩。
  • 相关阅读:
    Nginx是如何配置为 Web 服务器的【转载】
    BeanUtil工具类的使用
    数据库:XML,解析Dom4J
    jsp简单标签开发
    Java 反射 使用总结
    JAVA 对象序列化——Serializable
    使用Session防止表单重复提交
    EasyUi模糊匹配搜索框combobox
    Cookie工作原理
    COOKIE和SESSION有什么区别?
  • 原文地址:https://www.cnblogs.com/tracy/p/1851281.html
Copyright © 2011-2022 走看看