Получение буфера вывода из DBMS_OUTPUT.GET_LINES в C#

Я пытаюсь получить вывод метода DBMS_OUTPUT.PUT_LINE() в моем анонимном блоке PL / SQL через C#. Я рассмотрел пару других связанных вопросов здесь, но у меня все еще возникают проблемы. Код возврата выполнения анонимного блока возвращает -1, что должно быть правильным на основе документы.

Я устанавливаю DBMS_OUTPUT.ENABLE() на NULL, чтобы не устанавливать конкретный размер буфера, а затем использую метод DBMS_OUTPUT.GET_LINES(), чтобы получить строки из этого буфера.

Он ничего не возвращает в буфере (пустой OracleString[]) и возвращает строки 0. Мой анонимный блок PL / SQL очень прост, но должен работать с любым.

DECLARE
    lvsName VARCHAR2(6) := 'Oracle';
BEGIN
    DBMS_OUTPUT.PUT_LINE('Do you see me?');
    DBMS_OUTPUT.PUT_LINE('My name is: ' || lvsName);    
END;

Что мне не хватает?

using (OracleDataAdapter oda = new OracleDataAdapter())
using (OracleCommand cmd = new OracleCommand(sql, _connection))
{
    // Execute anonymous PL/SQL block
    cmd.CommandType = CommandType.Text;
    var res = cmd.ExecuteNonQuery();

    // Set output Buffer
    cmd.CommandText = "BEGIN DBMS_OUTPUT.ENABLE(NULL); END;";
    cmd.CommandType = CommandType.Text;
    cmd.ExecuteNonQuery();

    // Get output
    cmd.CommandText = "BEGIN DBMS_OUTPUT.GET_LINES(:outString, :numLines); END;";
    cmd.CommandType = CommandType.Text;
    cmd.Parameters.Clear();
    cmd.Parameters.Add(new OracleParameter("outString", OracleDbType.Varchar2, int.MaxValue, ParameterDirection.Output));
    cmd.Parameters["outString"].CollectionType = OracleCollectionType.PLSQLAssociativeArray;
    cmd.Parameters["outString"].Size = sql.Length;
    cmd.Parameters["outString"].ArrayBindSize = new int[sql.Length];
    cmd.Parameters.Add(new OracleParameter("numLines", OracleDbType.Int32, ParameterDirection.InputOutput));
    cmd.Parameters["numLines"].Value = 10; // Get 10 lines
    cmd.ExecuteNonQuery();

     int numLines = Convert.ToInt32(cmd.Parameters["numLines"].Value.ToString());
     string outString = string.Empty;

     // Try to get more lines until there are zero left
     while (numLines > 0)
     {
         for (int i = 0; i < numLines; i++)
         {
             OracleString s = (OracleString)cmd.Parameters["outString"].Value;
             outString += s.ToString();
         }

         cmd.ExecuteNonQuery();
         numLines = Convert.ToInt32(cmd.Parameters["numLines"].Value.ToString());
     }

     return outString;
}
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
11
0
3 256
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Я не говорю на C#, но я не вижу в вашем коде, где вы присваиваете значение переменной numLines.

  DBMS_OUTPUT.GET_LINES (
   lines       OUT     CHARARR,
   numlines    IN OUT  INTEGER);

Пример в plsql:

DECLARE
   v_array       DBMS_OUTPUT.CHARARR;
   v_lines   NUMBER;
BEGIN
   DBMS_OUTPUT.PUT_LINE ('aaaaa');
   DBMS_OUTPUT.put_line ('bbbb');
   DBMS_OUTPUT.put_line ('ccccc');
   v_lines := 1000; -- Number of lines you want to retrieve from the buffer.  
   DBMS_OUTPUT.GET_LINES (v_array, v_lines);

   DBMS_OUTPUT.put_line(v_lines); -- Lines retrieved from buffer.
   FOR idx IN nvl(v_array.FIRST,1) .. nvl(v_array.LAST,-1)
   LOOP
      DBMS_OUTPUT.put_line (v_array (idx));
   END LOOP;
END;

Параметр добавлен здесь: cmd.Parameters.Add(new OracleParameter("numLines", OracleDbType.Int32, ParameterDirection.InputOutput)); Значение устанавливается здесь: cmd.Parameters["numLines"].Value = 10; // Get 10 lines Я только что добавил фактическое добавление для .Value параметра и получил те же результаты.

Jimenemex 21.11.2018 16:03

Мне кажется, вы делаете это в неправильном порядке ...

// Execute anonymous PL/SQL block
cmd.CommandType = CommandType.Text;
var res = cmd.ExecuteNonQuery();

// Set output Buffer
cmd.CommandText = "BEGIN DBMS_OUTPUT.ENABLE(NULL); END;";
cmd.CommandType = CommandType.Text;
cmd.ExecuteNonQuery();

// Get output
cmd.CommandText = "BEGIN DBMS_OUTPUT.GET_LINES(:outString, :numLines); END;";

Между установкой (включением) DBMS_OUTPUT и получением вывода с помощью GET_LINES должна быть ваша команда записи, но вместо этого это первое, что вы выполняете.

Попробуйте изменить порядок. Сообщите мне, сработает ли это, потому что я не пробовал (я не привык к C# ... у меня это на Java).

Я пробовал это. Я получаю :numLines, чтобы вернуть 1, но значение :outString - DbNull. {null}.

Jimenemex 26.11.2018 17:02

Я все еще думаю, что вам следует писать после включения DBMS_OUTPUT и до вызова GET_LINES. Кроме того, в строке cmd.Parameters["outString"].ArrayBindSize = new int[sql.Length]; вы назначаете пустой массив для ArrayBindSize ... возможно, вам следует инициализировать каждую позицию этого массива. А еще в строке OracleString s = (OracleString)cmd.Parameters["outString"].Value; возвращением должен быть массив, не так ли? Можешь подтвердить?

Felypp Oliveira 26.11.2018 19:14
Ответ принят как подходящий

Основная проблема с вашим кодом заключалась в том, что он не устанавливал размер привязки для каждого элемента вашего выходного буфера. Кроме того, при получении результатов неправильно индексировался выходной буфер. И, наконец, порядок выполнения также играет роль: вы должны сначала включить свой вывод, прежде чем выполнять свой анонимный блок кода. Каждое внесенное изменение комментируется в следующем MCVE. Были внесены только необходимые изменения, чтобы он заработал.

static void Main(string[] args)
{
    string str = "User Id=xxx; password=xxx; Data Source=localhost:1521/xxx;";
    string sql = @"DECLARE lvsName VARCHAR2(6) := 'Oracle'; BEGIN  DBMS_OUTPUT.PUT_LINE('Do you see me?'); DBMS_OUTPUT.PUT_LINE('My name is: ' || lvsName); END;";

    OracleConnection _connection = new OracleConnection(str);

    try
    {
        _connection.Open();

        //adapter not being used
        //using (OracleDataAdapter oda = new OracleDataAdapter())

        using (OracleCommand cmd = new OracleCommand(sql, _connection))
        {
            // First enable buffer output
            // Set output Buffer
            cmd.CommandText = "BEGIN DBMS_OUTPUT.ENABLE(NULL); END;";
            cmd.CommandType = CommandType.Text;
            cmd.ExecuteNonQuery();

            // Then execute anonymous block
            // Execute anonymous PL/SQL block
            cmd.CommandText = sql;
            cmd.CommandType = CommandType.Text;
            var res = cmd.ExecuteNonQuery();


            // Get output
            cmd.CommandText = "BEGIN DBMS_OUTPUT.GET_LINES(:outString, :numLines); END;";
            cmd.CommandType = CommandType.Text;

            cmd.Parameters.Clear();

            cmd.Parameters.Add(new OracleParameter("outString", OracleDbType.Varchar2, int.MaxValue, ParameterDirection.Output));
            cmd.Parameters["outString"].CollectionType = OracleCollectionType.PLSQLAssociativeArray;
            cmd.Parameters["outString"].Size = sql.Length;
            cmd.Parameters["outString"].ArrayBindSize = new int[sql.Length];

            // set bind size for each array element
            for (int i = 0; i < sql.Length; i++)
            {
                cmd.Parameters["outString"].ArrayBindSize[i] = 32000;
            }


            cmd.Parameters.Add(new OracleParameter("numLines", OracleDbType.Int32, ParameterDirection.InputOutput));
            cmd.Parameters["numLines"].Value = 10; // Get 10 lines
            cmd.ExecuteNonQuery();

            int numLines = Convert.ToInt32(cmd.Parameters["numLines"].Value.ToString());
            string outString = string.Empty;

            // Try to get more lines until there are zero left
            while (numLines > 0)
            {
                for (int i = 0; i < numLines; i++)
                {
                    // use proper indexing here
                    //OracleString s = (OracleString)cmd.Parameters["outString"].Value;
                    OracleString s = ((OracleString[])cmd.Parameters["outString"].Value)[i];
                    outString += s.ToString();

                    // add new line just for formatting
                    outString += "\r\n";
                }

                cmd.ExecuteNonQuery();
                numLines = Convert.ToInt32(cmd.Parameters["numLines"].Value.ToString());
            }

            Console.WriteLine(outString);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }

    _connection.Close();
    _connection.Dispose();

    Console.WriteLine("Press RETURN to exit.");
    Console.ReadLine();
}

И выходной результат:

Do you see me?
My name is: Oracle

Press RETURN to exit.

Я знал, что был близок, спасибо!

Jimenemex 03.12.2018 15:20

Я не мог понять, почему это принятый ответ, поскольку он просто воспроизводит комментарий, который я добавил к своему ответу неделю назад. Может быть, потому что у вас здесь рабочий код, но в моем комментарии было достаточно ясно, где вы должны изменить свой код, и это были незначительные изменения, как вы отметили в своем комментарии выше. На самом деле, вы можете выбрать правильный ответ, и этот правильный, просто он не был первым, кто указал вам все решение, хотя не обязательно выбирать первый, просто было бы справедливо, имо, но я Рад помочь. Просто надеюсь, что в следующий раз смогу получить очки ...

Felypp Oliveira 03.12.2018 17:16

спасибо, этот ответ мне тоже помогает в проблеме это. так что прямо сейчас я пытаюсь понять здесь логику. Я понимаю, что параметр outstring предназначен для выходной строки, которую мы хотим напечатать, но какова именно функция numlines? как это влияет на результат? Я не могу понять по коду. Может кто-нибудь пролил свет на это. Спасибо..

HNA 12.03.2019 02:16

@HNA numlines использовался в этом случае для продолжения чтения из буфера до numlines, или значение количества строк, прочитанных Oracle, было 0. Так что я читал все строки, но только по 10 строк за раз.

Jimenemex 14.03.2019 15:12

Спасибо за ответ jsanalytics, который предоставил хорошую основу для решения. Однако есть некоторые проблемы с вышеуказанным решением, в основном связанные с использованием sql.Length во многих местах, где это не имеет смысла. Вот многоразовое решение, которое исправляет некоторые проблемы.

using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;

namespace MyNamespace
{
    public static class DbmsOutputHelper
    {
        public const int DefaultReadBatchSize = 10;

        public static void EnableDbmsOutput(this OracleConnection conn)
        {
            using (var cmd = conn.CreateCommand())
            {
                cmd.CommandText = "DBMS_OUTPUT.ENABLE";
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.ExecuteNonQuery();
            }
        }

        public static void DisableDbmsOutput(this OracleConnection conn)
        {
            using (var cmd = conn.CreateCommand())
            {
                cmd.CommandText = "DBMS_OUTPUT.DISABLE";
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.ExecuteNonQuery();
            }
        }

        public static List<string> ReadDbmsOutput(this OracleConnection conn, int readBatchSize = DefaultReadBatchSize)
        {
            if (readBatchSize <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(readBatchSize), "must be greater than zero");
            }

            using (var cmd = conn.CreateCommand())
            {
                cmd.CommandText = "DBMS_OUTPUT.GET_LINES";
                cmd.CommandType = CommandType.StoredProcedure;

                var linesParam = cmd.Parameters.Add(new OracleParameter("lines", OracleDbType.Varchar2, int.MaxValue, ParameterDirection.Output));
                linesParam.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
                linesParam.Size = readBatchSize;
                linesParam.ArrayBindSize = Enumerable.Repeat(32767, readBatchSize).ToArray();   // set bind size for each array element

                var numLinesParam = cmd.Parameters.Add(new OracleParameter("numlines", OracleDbType.Int32, ParameterDirection.InputOutput));

                var result = new List<string>();
                int numLinesRead;

                do
                {
                    numLinesParam.Value = readBatchSize;
                    cmd.ExecuteNonQuery();
                    numLinesRead = ((OracleDecimal)numLinesParam.Value).ToInt32();

                    var values = (OracleString[])linesParam.Value;

                    for (int i = 0; i < numLinesRead; i++)
                    {
                        result.Add(values[i].ToString());
                    }

                } while (numLinesRead == readBatchSize);

                return result;
            }
        }
    }
}

Другие вопросы по теме