使用大型CLOB从C#调用存储过程的问题

我不是第一个遇到这些问题的人,并会在下面列出一些参考文章,但我仍在寻找合适的解决方案。

我需要从C#Web服务调用存储过程(Oracle 10g数据库)。 Web服务器安装了Oracle 9i客户端,我使用的是Microsofts System.Data.OracleClient

该过程将XML作为CLOB。 当XML超过4000字节 (这可能是正常的用例)时,我偶然发现了以下错误:

ORA-01460 – 要求执行未实现或不合理的转换

我发现了这个 , 这个和这个post。

此外,我发现了一个很有前途的解决方法,它不直接从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 KB左右, 这种方法就会失败,这在我的应用程序中仍然很有可能。 这次错误源于PL / SQL编译器,它说:

ORA-06550:第1行,第87列: PLS-00172:字符串文字太长

经过一些研究,我得出结论,用我的第二种方法解决问题是不可行的。

根据上述post,我有以下两种选择。

  • 切换到ODP.NET (因为它应该是微软弃用的DB客户端中的一个错误)
  • 将CLOB插入表中并从那里读取存储过程

( 第一篇post说有些客户是马车,但是我的(9i)不属于上述10g / 11g版本的范围。)

你能否确认这是剩下的两个选择? 还是有其他方法可以帮助我吗?

只是为了澄清:XML最终不会保存在任何表中,但它由存储过程处理,该存储过程根据XML内容在某些表中插入一些记录。

我对这两个选项的考虑:

  • 切换到ODP.NET很困难,因为我必须将它安装在到目前为止我没有系统访问权限的Web服务器上,并且因为我们可能还想在客户端上部署这段代码,所以每个客户端都必须安装ODP.NET作为部署的一部分。
  • 绕过表会使客户端代码变得更加复杂,并且在调整/扩展PL / SQL例程的数据库上也需要付出相当大的努力。

我发现还有另一种解决问题的方法! 我的同事救了我的一天,指着我这个博客说:

在DbConnection上调用BeginTransaction时设置参数值。

它会更简单吗? 该博客与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 )。

对我来说,解决方案是使用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个字符,如果您需要执行更多操作,则必须使用存储过程。 然后,您最多限制为32KB,因此您必须“插入”插入。 布莱什。

-Oisin