2010-08-24 2 views
17

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

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

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

ORA-01460 - unimplemented or unreasonable conversion requested

Я нашел this, 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 килобайтов или так, который по-прежнему очень вероятен в моей заявке. На этот раз ошибка связана с/SQL компилятор PL, который говорит:

ORA-06550: line1, column 87: PLS-00172: string literal too long

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

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

  • Switch to ODP.NET (потому что она должна быть ошибкой в ​​устаревшем клиенте БД Microsoft Office)
  • Insert the CLOB into a table и сделать ХП чтение оттуда

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

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

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

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

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

BLOB или CLOB, опечатка. – Benny

+0

@Benny: Проблема существует для больших объектов LOB, следовательно, ссылки на BLOB-сообщения, но я боролся с CLOB. Я не могу найти опечатку в своем посте ... – chiccodoro

+0

'MyXML: = '{0}';' 'string.Format()' заменяет ''{0}'' на xml - однако, '' ... ''является строковым литералом, который имеет тип данных VARCHAR2 (а не' CLOB'). [PL/SQL имеет ограничение 32k] (http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/datatypes.htm#i43252) на 'VARCHAR2', поэтому это не работает. – MT0

ответ

12

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

Set the parameter value when BeginTransaction has already been called on the 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(); 
} 
+0

Поддерживает ли этот подход ограничение 32k? Или просто ограничение 4k? – mikemanne

+0

Боюсь, я не могу больше рассказать об этом из головы. Возможно, я мог бы, если бы вы спросили меня 1,5 года назад ... Я думаю, что я имею в виду, что он работает и в пределе 32k. Не могли бы вы сообщить мне, как только вы подтвердили/фальсифицировали его? – chiccodoro

+0

обязательно опубликует, если у нас появится шанс подтвердить/фальсифицировать. Должен любить воскрешать старые вопросы, а? :) – mikemanne

0

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

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

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

-Oisin

+0

Hi xOn, вы более или менее повторили то, что я уже написал в своем вопросе, не так ли? - Или это означает, что на самом деле вы бы подтвердили, что у меня нет других вариантов? – chiccodoro

+0

@chiccodoro - да, я подтверждаю, что вам не повезло, afaict. – x0n

+0

@ xOn- У меня есть хорошие новости! См. Мой новый ответ выше – chiccodoro

1

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; 
     } 
    } 
+1

Спасибо за подтверждение :-). Вы опубликовали достаточно кода. Может быть, вы можете немного рассказать о том, какова разница кода в моем коде или как вы сказали, разместив его? – chiccodoro

2

В моем случае решение 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) 
{ 
} 
+0

Привет, Кемалеттин, спасибо, что поделились.Первоначальный вопрос касался 'System.Data.OracleClient', но поскольку такая же проблема существует для' Oracle.DataAccess', это, безусловно, будет интересно некоторым читателям. – chiccodoro

+0

Даже этого решения было недостаточно в моем случае. Мне пришлось записывать данные в clob кусками 1 ko, используя цикл и метод clob.Append –

Смежные вопросы