Проблемы вызова хранимой процедуры из C # с большим CLOB - PullRequest
18 голосов
/ 24 августа 2010

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

Мне нужно вызвать хранимую процедуру (база данных Oracle 10g) из веб-службы C #. На веб-сервере установлен клиент Oracle 9i, и я использую Microsoft 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 ]

13 голосов
/ 25 августа 2010

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

Установите значение параметра, когда 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();
}
3 голосов
/ 08 июля 2013

В моем случае решение Чиккодоро не сработало. Я использую 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)
{
}
1 голос
/ 24 мая 2012

Чиккодоро прав.

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;
        }
    }
0 голосов
/ 24 августа 2010

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

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

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

-Oisin

...