Тайм-аут для метода OracleDataReader.Read

Класс ODP.NET OracleCommand имеет свойство CommandTimeout, которое можно использовать для принудительного тайм-аута выполнения команды. Это свойство работает в ситуациях, когда CommandText является оператором SQL. Пример кода используется для иллюстрации этого свойства в действии. В первоначальной версии кода CommandTimeout установлен в ноль, что говорит ODP.NET, что не нужно устанавливать тайм-аут.

using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Oracle.DataAccess.Client;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            using (OracleConnection con = new OracleConnection("User ID=xxxx; Password=xxxx; Data Source=xxxx;"))
            using (OracleCommand cmd = new OracleCommand())
            {
                con.Open();                
                cmd.Connection = con;

                Console.WriteLine("Executing Query...");

                try
                {
                    cmd.CommandTimeout = 0;

                    // Data set SQL:
                    cmd.CommandText = "";
                    cmd.CommandType = System.Data.CommandType.Text;

                    Stopwatch watch1 = Stopwatch.StartNew();
                    OracleDataReader reader = cmd.ExecuteReader();
                    watch1.Stop();
                    Console.WriteLine("Query complete.  Execution time: {0} ms", watch1.ElapsedMilliseconds);

                    int counter = 0;
                    Stopwatch watch2 = Stopwatch.StartNew();
                    if (reader.Read()) counter++;
                    watch2.Stop();
                    Console.WriteLine("First record read: {0} ms", watch2.ElapsedMilliseconds);

                    Stopwatch watch3 = Stopwatch.StartNew();
                    while (reader.Read())
                    {
                        counter++;
                    }
                    watch3.Stop();
                    Console.WriteLine("Records 2..n read: {0} ms", watch3.ElapsedMilliseconds);
                    Console.WriteLine("Records read: {0}", counter);
                }
                catch (OracleException ex)
                {
                    Console.WriteLine("Exception was thrown: {0}", ex.Message);
                }

                Console.WriteLine("Press any key to continue...");
                Console.Read();
            }
        }
    }
}

Пример вывода для приведенного выше кода показан ниже:

Executing Query...
Query complete.  Execution time: 8372 ms
First record read: 3 ms
Records 2..n read: 1222 ms
Records read: 20564
Press any key to continue...

Если я изменю CommandTimeout на что-то вроде 3 ...

cmd.CommandTimeout = 3;

... тогда выполнение того же кода приводит к следующему выводу:

Executing Query...
Exception was thrown: ORA-01013: user requested cancel of current operation
Press any key to continue...

Вызов хранимой процедуры, которая возвращает курсор ref, - другой вопрос. Рассмотрим тестовый процесс ниже (исключительно для целей тестирования):

PROCEDURE PROC_A(i_sql VARCHAR2, o_cur1 OUT SYS_REFCURSOR)
is
begin

    open o_cur1
    for
    i_sql;

END PROC_A;

Пример кода ниже может быть использован для вызова хранимой процедуры. Обратите внимание, что он устанавливает для CommandTimeout значение 3.

using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Oracle.DataAccess.Client;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            using (OracleConnection con = new OracleConnection("User ID=xxxx; Password=xxxx; Data Source=xxxx;"))
            using (OracleCommand cmd = new OracleCommand())
            {
                con.Open();                
                cmd.Connection = con;

                Console.WriteLine("Executing Query...");

                try
                {
                    cmd.CommandTimeout = 3;

                    string sql = "";
                    cmd.CommandText = "PROC_A";
                    cmd.CommandType = System.Data.CommandType.StoredProcedure;
                    cmd.Parameters.Add(new OracleParameter("i_sql", OracleDbType.Varchar2) { Direction = ParameterDirection.Input, Value = sql });
                    cmd.Parameters.Add(new OracleParameter("o_cur1", OracleDbType.RefCursor) { Direction = ParameterDirection.Output });

                    Stopwatch watch1 = Stopwatch.StartNew();
                    OracleDataReader reader = cmd.ExecuteReader();
                    watch1.Stop();
                    Console.WriteLine("Query complete.  Execution time: {0} ms", watch1.ElapsedMilliseconds);

                    int counter = 0;
                    Stopwatch watch2 = Stopwatch.StartNew();
                    if (reader.Read()) counter++;
                    watch2.Stop();
                    Console.WriteLine("First record read: {0} ms", watch2.ElapsedMilliseconds);

                    Stopwatch watch3 = Stopwatch.StartNew();
                    while (reader.Read())
                    {
                        counter++;
                    }
                    watch3.Stop();
                    Console.WriteLine("Records 2..n read: {0} ms", watch3.ElapsedMilliseconds);
                    Console.WriteLine("Records read: {0}", counter);
                }
                catch (OracleException ex)
                {
                    Console.WriteLine("Exception was thrown: {0}", ex.Message);
                }

                Console.WriteLine("Press any key to continue...");
                Console.Read();
            }
        }
    }
}

Пример вывода из кода выше показан ниже:

Executing Query...
Query complete.  Execution time: 34 ms
First record read: 8521 ms
Records 2..n read: 1014 ms
Records read: 20564
Press any key to continue...

Обратите внимание, что время выполнения очень короткое (34 мс) и исключение тайм-аута не было выдано. Производительность, которую мы здесь видим, заключается в том, что оператор SQL для курсора ref не выполняется до первого вызова метода OracleDataReader.Read. Когда первый вызов Read () сделан для чтения первой записи из refcursor, тогда происходит снижение производительности из-за длительного выполнения запроса.

Поведение, которое яПроиллюстрированное означает, что свойство OracleCommand.CommandTimeout может 't использоваться для отмены длинного запущенного запроса, связанного с курсором ref. Я'Я не знаю ни одного свойства в ODP.NET, которое можно использовать для ограничения времени выполнения SQL-курсора ref в этой ситуации. Кто-нибудь есть какие-либо предложения о том, как выполнение короткого оператора SQL курсора ref может быть короткое замыкание через определенное время? I '

Ответы на вопрос(2)

Ваш ответ на вопрос