J'essaie d'obtenir la sortie de la méthode DBMS_OUTPUT.PUT_LINE() dans mon bloc PL / SQL anonyme via C #. J'ai examiné quelques autres questions connexes ici, mais j'ai toujours des problèmes. Le code de retour de l'exécution du bloc anonyme renvoie -1, ce qui devrait être correct sur la base de docs.

Je règle le DBMS_OUTPUT.ENABLE() sur NULL afin de ne pas définir une taille de tampon spécifique, puis j'utilise la méthode DBMS_OUTPUT.GET_LINES() afin d'obtenir les lignes de ce tampon.

Il ne renvoie rien dans le tampon (An vide OracleString[]) et renvoie 0 lignes. Mon bloc PL / SQL anonyme est simple comme ça, mais devrait fonctionner pour tout.

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

Qu'est-ce que je rate?

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;
}
10
Jimenemex 21 nov. 2018 à 01:09

4 réponses

Meilleure réponse

Le principal problème avec votre code était qu'il ne définissait pas la taille de liaison pour chaque élément de votre tampon de sortie. De plus, il n'indexait pas correctement le tampon de sortie lors de la récupération des résultats. Et enfin, l'ordre d'exécution joue également un rôle: vous devez d'abord activer votre sortie avant d'exécuter votre bloc de code anonyme. Chaque modification apportée est commentée dans le MCVE suivant. Seules les modifications nécessaires pour le faire fonctionner ont été apportées.

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();
}

Et le résultat de sortie est:

Do you see me?
My name is: Oracle

Press RETURN to exit.
4
jsanalytics 3 déc. 2018 à 17:40

Merci pour la réponse ci-dessus par jsanalytics qui a fourni une bonne base pour une solution. Il y a cependant quelques problèmes avec la solution ci-dessus, principalement liés à l'utilisation de sql.Length dans de nombreux endroits où cela n'a pas de sens. Voici une solution réutilisable qui corrige certains des problèmes.

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;
            }
        }
    }
}
1
ajz 10 déc. 2019 à 13:11

Je ne parle pas C # mais je ne vois pas dans votre code où vous attribuez une valeur à la variable numLines.

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

Exemple en 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;
1
Arkadiusz Łukasiewicz 21 nov. 2018 à 12:08

Il me semble que vous le faites dans le mauvais ordre ...

// 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;";

Entre la configuration (l'activation) de DBMS_OUTPUT et l'obtention de la sortie en utilisant GET_LINES devrait être votre commande d'écriture, mais à la place, c'est la première chose que vous exécutez.

Essayez de changer l'ordre. Faites-moi savoir si cela fonctionne parce que je ne l'ai pas essayé (je ne suis pas habitué à C # ... je l'ai en Java).

2
Felypp Oliveira 26 nov. 2018 à 13:52