从 C# 使用大型 CLOB 调用存储过程时出现问题
我不是第一个遇到这些问题的人,并将在下面列出一些参考帖子,但我仍在寻找适当的解决方案。
我需要从 C# Web 服务调用存储过程(Oracle 10g 数据库)。 Web 服务器安装了 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 KB,这种方法就会失败左右,这在我的申请中仍然很有可能。这次错误源于 PL/SQL 编译器,它表示:
ORA-06550: 第 1 行,第 87 列:PLS-00172: 字符串文字太长
经过一番研究,我得出结论,用第二种方法解决问题根本不可行。
根据上述帖子,我有以下两个选择。
- 切换到 ODP.NET(因为这应该是 Microsoft 已弃用的数据库客户端中的一个错误)
- 将 CLOB 插入表 并从那里读取存储过程
(第一篇文章说某些客户端有错误,但我的(9i )不属于上述的 10g/11g 版本范围。)
您能否确认这是仅剩的两个选项?或者还有其他方法可以帮助我吗?
只是澄清一下:XML不会最终保存在任何表中,但它是由存储过程处理的,该存储过程会在其中插入一些记录一些基于 XML 内容的表。
我对这两个选项的考虑:
- 切换到 ODP.NET 很困难,因为我必须将其安装在到目前为止我没有系统访问权限的 Web 服务器上,并且因为我们可能还想在客户端上部署该代码段,因此每个客户端都必须安装 ODP.NET 作为部署的一部分。
- 绕过表使客户端代码变得相当复杂,并且在数据库调整/扩展 PL/SQL 例程方面也花费了相当多的精力。
I'm not the first to have these issues, and will list some reference posts below, but am still looking for a proper solution.
I need to call a stored procedure (Oracle 10g database) from a C# web service. The web server has an Oracle 9i client installed and I am using Microsofts System.Data.OracleClient
.
The procedure takes an XML as a CLOB. When the XML was over 4000 Bytes (which is likely in a normal use case), I stumbled over the following error:
ORA-01460 - unimplemented or unreasonable conversion requested
I've found this, this and this post.
Further I found a promising workaround which doesn't call the stored procedure directly from C# but defines a piece of anonymous PL/SQL code instead. This code is run as an OracleCommand. The XML is embedded as a string literal and the procedure call is done from within that piece of code:
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();
Unfortunately, this approach now fails as soon as the XML is over 32 KBytes or so, which still is very likely in my application. This time the error stems from the PL/SQL compiler which says:
ORA-06550: line1, column 87: PLS-00172: string literal too long
After some research I conclude that it's simply not feasible to solve the problem with my second approach.
Following the above-mentioned posts I have the following two options.
- Switch to ODP.NET (because it is supposed to be a bug in Microsoft's deprecated DB client)
- Insert the CLOB into a table and make the stored proc read from there
(The first post said some clients are buggy, but mine (9i) does not fall in the mentioned range of 10g/11g versions.)
Can you confirm that these are the only two options left? Or is there another way to help me out?
Just to clarify: the XML won't eventually be saved in any table, but it is processed by the stored procedure which inserts some records in some table based on the XML contents.
My considerations about the two options:
- Switching to ODP.NET is difficult because I have to install it on a web server on which I don't have system access so far, and because we might also want to deploy the piece of code on clients, so each client would have to install ODP.NET as part of the deployment.
- The detour over a table makes the client code quite a bit more complicated and also takes quite some effort on the database adapting/extending the PL/SQL routines.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我发现还有另一种方法可以解决该问题!我的同事帮我指点
能更简单一点吗?该博客与
Oracle.DataAccess
相关,但它也适用于System.Data.OracleClient
。实际上这意味着:
I found that there is another way to work around the problem! My fellow employee saved my day pointing me to this blog, which says:
Could it be simpler? The blog relates to
Oracle.DataAccess
, but it works just as well forSystem.Data.OracleClient
.In practice this means:
就我而言,chiccodoro 的解决方案不起作用。我正在使用 ODP.NET (
Oracle.DataAccess
)。对我来说,解决方案是使用 OracleClob 对象。
In my case, chiccodoro's solution did not work. I'm using ODP.NET (
Oracle.DataAccess
).For me the solution is using
OracleClob
object.我想我只是用谷歌搜索这个来让你获得便宜的积分,但是这里有一个很好的解释:
http://www.orafaq.com/forum/t/48485/0/
基本上你不能在字符串文字中使用超过4000个字符,如果你需要做更多,你必须使用存储过程。然后,您的最大大小限制为 32KB,因此您必须对插入内容进行“分块”。布莱赫。
-奥辛
I guess I just googled this for you to get cheap points, but there's a great explanation here:
http://www.orafaq.com/forum/t/48485/0/
Basically you cannot use more than 4000 chars in a string literal, and if you need to do more, you must use a stored procedure. Then, you are limited to 32KB at max so you have to "chunk" the inserts. Blech.
-Oisin
奇科多罗是对的。
chiccodoro is right.