Я пытаюсь получить вывод метода 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;
}





Я не говорю на 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;
Мне кажется, вы делаете это в неправильном порядке ...
// 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}.
Я все еще думаю, что вам следует писать после включения DBMS_OUTPUT и до вызова GET_LINES. Кроме того, в строке cmd.Parameters["outString"].ArrayBindSize = new int[sql.Length]; вы назначаете пустой массив для ArrayBindSize ... возможно, вам следует инициализировать каждую позицию этого массива. А еще в строке OracleString s = (OracleString)cmd.Parameters["outString"].Value; возвращением должен быть массив, не так ли? Можешь подтвердить?
Основная проблема с вашим кодом заключалась в том, что он не устанавливал размер привязки для каждого элемента вашего выходного буфера. Кроме того, при получении результатов неправильно индексировался выходной буфер. И, наконец, порядок выполнения также играет роль: вы должны сначала включить свой вывод, прежде чем выполнять свой анонимный блок кода. Каждое внесенное изменение комментируется в следующем 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.
Я знал, что был близок, спасибо!
Я не мог понять, почему это принятый ответ, поскольку он просто воспроизводит комментарий, который я добавил к своему ответу неделю назад. Может быть, потому что у вас здесь рабочий код, но в моем комментарии было достаточно ясно, где вы должны изменить свой код, и это были незначительные изменения, как вы отметили в своем комментарии выше. На самом деле, вы можете выбрать правильный ответ, и этот правильный, просто он не был первым, кто указал вам все решение, хотя не обязательно выбирать первый, просто было бы справедливо, имо, но я Рад помочь. Просто надеюсь, что в следующий раз смогу получить очки ...
спасибо, этот ответ мне тоже помогает в проблеме это. так что прямо сейчас я пытаюсь понять здесь логику. Я понимаю, что параметр outstring предназначен для выходной строки, которую мы хотим напечатать, но какова именно функция numlines? как это влияет на результат? Я не могу понять по коду. Может кто-нибудь пролил свет на это. Спасибо..
@HNA numlines использовался в этом случае для продолжения чтения из буфера до numlines, или значение количества строк, прочитанных Oracle, было 0. Так что я читал все строки, но только по 10 строк за раз.
Спасибо за ответ 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;
}
}
}
}
Параметр добавлен здесь:
cmd.Parameters.Add(new OracleParameter("numLines", OracleDbType.Int32, ParameterDirection.InputOutput));Значение устанавливается здесь:cmd.Parameters["numLines"].Value = 10; // Get 10 linesЯ только что добавил фактическое добавление для.Valueпараметра и получил те же результаты.