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

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

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

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

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

Я нашел this, This и это сообщение.

Кроме того, я нашел многообещающий обходной путь, который не вызывает хранимую процедуру непосредственно из 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 в базе данных.

person chiccodoro    schedule 24.08.2010    source источник
comment
@Benny: Проблема существует для ans LOB, отсюда и упомянутые сообщения BLOB, но я боролся с CLOB. Я не нашел ни одной опечатки в своем посте ...   -  person chiccodoro    schedule 15.09.2010
comment
MyXML := '{0}'; string.Format() заменяет '{0}' на xml, однако '...' является строковым литералом типа данных VARCHAR2 (а не CLOB). PL / SQL имеет ограничение 32 КБ на VARCHAR2s, поэтому это не удается.   -  person MT0    schedule 12.07.2016


Ответы (4)


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

Установите значение параметра, когда 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();
}
person chiccodoro    schedule 25.08.2010
comment
Обходит ли этот подход ограничение 32k? Или просто предел 4к? - person mikemanne; 07.06.2012
comment
Боюсь, я больше не могу сказать вам это из головы - я бы, вероятно, мог, если бы вы спросили меня 1,5 года назад ... Я думаю, что нужно иметь в виду, что это работает и в районе предела 32k. Не могли бы вы сообщить мне, как только подтвердите / сфальсифицируете его? - person chiccodoro; 08.06.2012
comment
обязательно опубликуем, если у нас будет возможность подтвердить / опровергнуть. Должен любить воскрешать старые вопросы, а? :) - person mikemanne; 11.06.2012
comment
Мое мнение по старому вопросу состоит в том, что старых вопросов нет. Это не форум, а сайт вопросов и ответов, поэтому в идеале конкретный вопрос должен быть найден ровно один раз, а ответы к нему прилагаются. - person chiccodoro; 12.06.2012
comment
Я только что пробовал это со строкой из 40 тыс. Символов, и она отлично работает. - person Neil Vass; 10.12.2012
comment
Обновленная ссылка: henbo.wordpress. ru / 2007/12/05 / - person Samah; 17.04.2013
comment
Работал для меня над .NET 4.5 и Oracle 12c, загружая около 1 МБ XML. - person wweicker; 26.01.2016

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

Для меня решение использует объект OracleClob.

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

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

//connection should be open!
OracleClob clob = new OracleClob(_oracleConnection);
// xmlData: a string with way more than 4000 chars
clob.Write(xmlData.ToArray(),0,xmlData.Length);
xmlParam.Value = clob; 

try
{
    cmd.ExecuteNonQuery();
}
catch (OracleException e)
{
}
person Kemalettin Erbakırcı    schedule 08.07.2013
comment
Привет, Кемалеттин, спасибо, что поделились. Первоначальный вопрос касался System.Data.OracleClient, но поскольку такая же проблема существует для Oracle.DataAccess, это, безусловно, будет интересно некоторым читателям. - person chiccodoro; 09.07.2013
comment
В моем случае даже этого решения было недостаточно. Мне пришлось записывать данные в clob кусками по 1 ко, используя цикл и метод clob.Append - person Kevin Gosse; 26.08.2013

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;
        }
    }
person Vincent    schedule 24.05.2012
comment
Спасибо за подтверждение :-). Вы разместили довольно много кода. Может быть, вы могли бы немного рассказать о том, в чем отличие вашего кода от моего, или о том, что вы намеревались сказать, разместив его? - person chiccodoro; 09.07.2013