【问题标题】:Issues calling stored procedure from C# with large CLOB使用大 CLOB 从 C# 调用存储过程的问题
【发布时间】:2011-04-03 05:56:22
【问题描述】:

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

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

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

ORA-01460 - 请求未实现或不合理的转换

我找到了thisthisthis 的帖子。

此外,我发现了一个有前途的解决方法,它不直接从 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:字符串文字太长

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

根据上述帖子,我有以下两个选择。

The first post 说有些客户端有问题,但我的 (9i) 不在上述 10g/11g 版本范围内。)

您能确认这是仅剩的两个选项吗?或者有其他方法可以帮助我吗?

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

我对这两个选项的考虑:

  • 切换到 ODP.NET 很困难,因为我必须将它安装在到目前为止我没有系统访问权限的 Web 服务器上,而且因为我们可能还想在客户端上部署这段代码,所以每个客户端必须在部署过程中安装 ODP.NET。
  • 绕过表使客户端代码变得相当复杂,并且在数据库适应/扩展 PL/SQL 例程方面也花费了相当多的精力。

【问题讨论】:

  • @Benny:ans LOB 存在问题,因此引用了 BLOB 帖子,但我在 CLOB 方面遇到了困难。我在我的帖子中找不到任何错字...
  • MyXML := '{0}'; string.Format() 正在用 xml 替换 '{0}' - 但是,'...' 是一个字符串文字,它属于 VARCHAR2 数据类型(不是 CLOB)。 PL/SQL has a 32k limit on VARCHAR2s 这就是失败的原因。

标签: c# oracle stored-procedures clob system.data.oracleclient


【解决方案1】:

我想我只是在谷歌上搜索了这个以获取便宜的积分,但这里有一个很好的解释:

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

基本上你不能在一个字符串文字中使用超过 4000 个字符,如果你需要做更多,你必须使用存储过程。然后,您的最大限制为 32KB,因此您必须“分块”插入。布莱赫。

-奥辛

【讨论】:

  • 嗨 xOn,您或多或少重复了我在问题中已经写过的内容,不是吗? - 或者这是否意味着事实上你会确认我没有其他选择?
  • @chiccodoro - 是的,我确认你不走运,afaict。
【解决方案2】:

我发现另一种解决问题的方法!我的同事节省了我的时间,将我指向this blog,上面写着:

设置参数值时 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();
}

【讨论】:

  • 这种方法是否也适用于 32k 限制?还是只有 4k 限制?
  • 恐怕我不能再告诉你这个了 - 如果你在 1.5 年前问我,我可能会告诉你......我想记住它在 32k 左右工作也有限制。验证/伪造后能否告诉我?
  • 我对老问题的看法是没有老问题。这不是一个论坛,而是一个 Q/A 网站,因此理想情况下,一个特定的问题只能找到一次,并且答案会附在上面。
  • 我刚刚用一个 40k 的字符串试了一下,效果很好。
【解决方案3】:

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

【讨论】:

  • 感谢您的确认 :-)。您已经发布了相当多的代码。也许您可以稍微扩展一下您的代码与我的代码的区别是什么,或者您想通过发布它来表达什么?
【解决方案4】:

就我而言,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)
{
}

【讨论】:

  • 嗨 Kemalettin,感谢您的分享。最初的问题是关于System.Data.OracleClient,但由于Oracle.DataAccess 存在同样的问题,这肯定会引起一些读者的兴趣。
  • 在我的情况下,即使是这个解决方案也不够。我必须使用循环和clob.Append 方法以 1 ko 的块将数据写入 clob 中
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-25
  • 1970-01-01
  • 2012-06-05
相关资源
最近更新 更多