Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Tempo limite para o método OracleDataReader.Read


Aqui está a solução com a qual eu finalmente fui. É apenas um método de extensão para a classe OracleDataReader. Este método tem um valor de tempo limite e uma função de retorno de chamada como parâmetros. A função de retorno de chamada normalmente (se nem sempre) seria OracleCommand.Cancel.
namespace ConsoleApplication1
{
    public static class OracleDataReaderExtensions
    {
        public static bool Read(this OracleDataReader reader, int timeout, Action cancellationAction)
        {
            Task<bool> task = Task<bool>.Factory.StartNew(() => 
                {
                    try
                    {
                        return reader.Read();
                    }
                    catch (OracleException ex)
                    {
                        // When cancellationAction is called below, it will trigger 
                        // an ORA-01013 error in the Read call that is still executing.
                        // This exception can be ignored as we're handling the situation
                        // by throwing a TimeoutException.
                        if (ex.Number == 1013)
                        {
                            return false;
                        }
                        else
                        {
                            throw;
                        }
                    }
                });

            try
            {
                if (!task.Wait(timeout))
                {
                    // call the cancellation callback function (i.e. OracleCommand.Cancel())
                    cancellationAction();

                    // throw an exception to notify calling code that a timeout has occurred
                    throw new TimeoutException("The OracleDataReader.Read operation has timed-out.");
                }
                return task.Result;
            }
            catch (AggregateException ae)
            {
                throw ae.Flatten();
            }
        }
    }
}

Aqui está um exemplo de como ele pode ser usado.
namespace ConsoleApplication1
{
    class Program
    {
        static string constring = "User ID=xxxx; Password=xxxx; Data Source=xxxx;";

        static void Main(string[] args)
        {
            using (OracleConnection con = new OracleConnection(constring))
            using (OracleCommand cmd = new OracleCommand())
            {
                cmd.Connection = con;
                con.Open();

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

                string sql = "<some long running 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 });

                try
                {
                    // execute command and get reader for ref cursor
                    OracleDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);

                    // read first record; this is where the ref cursor SQL gets evaluated
                    Console.WriteLine("Reading first record...");
                    if (reader.Read(3000, cmd.Cancel)) { }

                    // read remaining records
                    Console.WriteLine("Reading records 2 to N...");
                    while (reader.Read(3000, cmd.Cancel)) { }
                }
                catch (TimeoutException ex)
                {
                    Console.WriteLine("Exception: {0}", ex.Message);
                }

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

E aqui está um exemplo da saída.
Executing Query...
Reading first record...
Exception: The OracleDataReader.Read operation has timed-out.
Press any key to continue...