Проблемы с вызовом хранимой процедуры из C# с большим CLOB
Я не первый, кто имеет эти проблемы, и перечислю некоторые справочные сообщения ниже, но все еще ищу правильное решение.
Мне нужно вызвать хранимую процедуру (Oracle 10G database) из веб-службы 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: строковый литерал слишком длинный
Следуя вышеизложенному сообщения у меня есть следующие два варианта.
- переключиться на ODP.NET (поскольку предполагается, что это ошибка в устаревшем клиенте БД Microsoft)
- вставьте CLOB в таблицу и сделайте сохраненный proc считанным оттуда
(первый пост сказал, что некоторые клиенты глючат, но мой (9i) не попадает в упомянутый диапазон версий 10g/11g.)
Можете ли вы подтвердить, что это единственные два оставшихся варианта? Или есть есть другой способ помочь мне?
Просто уточним: XML в конечном счете не будет сохранен в какой-либо таблице, но он обрабатывается хранимой процедурой, которая вставляет некоторые записи в некоторую таблицу на основе содержимого XML.
Мои соображения о двух вариантах:
- переключение на ODP.NET это сложно, потому что я должен установить его на веб-сервере, на котором у меня пока нет доступа к системе, и потому что мы также можем захотеть развернуть этот фрагмент кода на клиенты, так что каждый клиент должен был бы установить ODP.NET как часть развертывания.
- объезд по таблице делает клиентский код довольно сложным, а также требует довольно больших усилий по адаптации/расширению процедур PL/SQL в базе данных.
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(); }
В моем случае решение чиккодоро не сработало. Я использую 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) { }
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 символов в строковом литерале, и если вам нужно сделать больше, вы должны использовать хранимую процедуру. Затем, вы ограничены до 32 КБ на МАКС, так что вы должны "чунк" вставки. Блич.
- Oisin