问题
I'm trying to get the output from the DBMS_OUTPUT.PUT_LINE()
method in my anonymous PL/SQL block through C#. I've looked at a couple of other related questions here, but am still having trouble. The return code of executing the anonymous block is returning -1
, which should be correct based on the docs.
I'm setting the DBMS_OUTPUT.ENABLE()
to NULL
in order to not set a specific buffer size, then using the DBMS_OUTPUT.GET_LINES()
method in order to get the lines from that buffer.
It returns nothing in the buffer (An empty OracleString[]
) and returns 0
lines. My anonymous PL/SQL block is simple like this, but should work for any.
DECLARE
lvsName VARCHAR2(6) := 'Oracle';
BEGIN
DBMS_OUTPUT.PUT_LINE('Do you see me?');
DBMS_OUTPUT.PUT_LINE('My name is: ' || lvsName);
END;
What am I missing?
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;
}
回答1:
The main problem with your code was that it was not setting the bind size for each element of your output buffer. Also it was not properly indexing the output buffer when retrieving the results. And finally, the order of execution also plays a role: you have to first enable your output before executing your anonymous block of code. Every single change made is commented in the following MCVE. Only necessary changes to get it working were made.
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();
}
And the output result is:
Do you see me?
My name is: Oracle
Press RETURN to exit.
回答2:
It looks to me that you're doing it in the wrong order...
// 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;";
Between setting (enabling) the DBMS_OUTPUT
and getting the output using GET_LINES
should be your write command, but instead, it's the first thing you're executing.
Try to change the order. Let me know if it works cause I didn't try it (I'm not used to C#... I have it in Java).
回答3:
I don't speak C# but i don't see in your code where you are assigning value to the numLines variable.
DBMS_OUTPUT.GET_LINES (
lines OUT CHARARR,
numlines IN OUT INTEGER);
Example in 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;
来源:https://stackoverflow.com/questions/53402356/getting-output-buffer-from-dbms-output-get-lines-in-c-sharp