Проблемы с вызовом хранимой процедуры из C# с большим CLOB

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

мне нужно вызвать хранимую процедуру (базу данных Oracle 10g) из веб-службы C#. На веб-сервере установлен клиент Oracle 9i, и я использую Microsofts System.Data.OracleClient.

процедура принимает XML как CLOB. Когда XML был более 4000 байт (что, вероятно, в обычном случае использования), я наткнулся на следующее ошибка:

ORA-01460 - неосуществленное или необоснованное преобразование запрошено

Я нашел этой, этой и этой пост.

далее я нашел многообещающее обходное решение, которое не вызывает хранимую процедуру непосредственно из C#, но вместо этого определяет фрагмент анонимного кода PL/SQL. Этот код выполняется как OracleCommand. XML внедрен в виде строкового литерала и процедуры вызов выполняется из этого фрагмента кода:

private const string LoadXml =
    "DECLARE " +
    "  MyXML CLOB; " +
    "  iStatus INTEGER; " +
    "  sErrMessage VARCHAR2(2000); " +
    "BEGIN " +
    "  MyXML := '{0}'; " +
    "  iStatus := LoadXML(MyXML, sErrMessage); " +
    "  DBMS_OUTPUT.ENABLE(buffer_size => NULL); " +
    "  DBMS_OUTPUT.PUT_LINE(iStatus || ',' || sErrMessage); " +
    "END;";
OracleCommand oraCommand = new OracleCommand(
    string.Format(LoadXml, xml), oraConnection);
oraCommand.ExecuteNonQuery();

к сожалению, этой подход терпит неудачу, как только XML превышает 32 Кбайт или так, что все еще очень вероятно в моем приложении. На этот раз ошибка связана с компилятором PL / SQL, который говорит:

ORA-06550: строка 1, колонка 87:PLS-00172: строковый литерал слишком длинный

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

после вышеупомянутых сообщений у меня есть следующие два варианта.

(первый пост сказал, что некоторые клиенты глючат, но мой (9i) не падение в упомянутом диапазоне версий 10g/11g.)

можете ли вы подтвердить, что это единственные два варианта? Или есть другой способ мне помочь?

просто чтобы уточнить: XML не в конечном итоге сохраняется в любой таблице, но обрабатывается хранимой процедурой, которая вставляет некоторые записи в некоторую таблицу на основе содержимого XML.

мои соображения о двух вариантах:

  • переключение к ODP.NET сложно, потому что я должен установить его на веб-сервере, на котором у меня пока нет доступа к системе, и потому что мы также можем захотеть развернуть часть кода на клиентах, поэтому каждому клиенту придется установить ODP.NET в рамках развертывания.
  • объезд по таблице делает клиентский код немного более сложным, а также требует довольно больших усилий по адаптации/расширению базы данных подпрограмм PL/SQL.

4 ответов


Я нашел, что там is другой способ обойти эту проблему! Мой коллега спас мой день, указав мне на этот блог, в котором говорится:

установите значение параметра при BeginTransaction уже вызывается DbConnection.

может быть проще? Блог относится к Oracle.DataAccess, но он работает так же хорошо для System.Data.OracleClient.

на практике это означает:

varcmd = new OracleCommand("LoadXML", _oracleConnection);
cmd.CommandType = CommandType.StoredProcedure;

var xmlParam = new OracleParameter("XMLFile", OracleType.Clob);
cmd.Parameters.Add(xmlParam);

// DO NOT assign the parameter value yet in this place

cmd.Transaction = _oracleConnection.BeginTransaction();
try
{
    // Assign value here, AFTER starting the TX
    xmlParam.Value = xmlWithWayMoreThan4000Characters;

    cmd.ExecuteNonQuery();
    cmd.Transaction.Commit();
}
catch (OracleException)
{
    cmd.Transaction.Rollback();
}

В моем случае, решение chiccodoro не работать. Я использую ODP.NET (Oracle.DataAccess ).

для меня решение использует


chiccodoro прав.

public static int RunProcedure(string storedProcName, IDataParameter[] parameters)
    {
        using (OracleConnection connection = new OracleConnection(connectionString))
        {
            int rowsAffected;

            OracleCommand command = new OracleCommand(storedProcName, connection);
            command.CommandText = storedProcName;
            command.CommandType = CommandType.StoredProcedure;
            foreach (OracleParameter parameter in parameters)
            {
                command.Parameters.Add(parameter);
            }
            connection.Open();

            try
            {
                // start transaction
                command.Transaction = connection.BeginTransaction();
                rowsAffected = command.ExecuteNonQuery();
                command.Transaction.Commit();
            }
            catch (System.Exception ex)
            {
                command.Transaction.Rollback();
                throw ex;
            }

            connection.Close();
            return rowsAffected;
        }
    }

думаю, я просто погуглил это для вас, чтобы получить дешевые очки, но здесь есть отличное объяснение:

http://www.orafaq.com/forum/t/48485/0/

в основном вы не можете использовать более 4000 символов в строковом литерале, и если вам нужно сделать больше, вы должны использовать хранимую процедуру. Затем вы ограничены 32KB на макс., поэтому вам нужно "откусить" вставки. Блех.

- Oisin