21

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

我需要从 C# Web 服务调用存储过程(Oracle 10g 数据库)。Web 服务器安装了 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 KB左右,这种方法就会失败,这在我的应用程序中仍然很可能。这次错误源于 PL/SQL 编译器,它说:

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

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

在上述帖子之后,我有以下两个选择。

第一篇文章说有些客户端有问题,但我的(9i)不在上述的 10g/11g 版本范围内。)

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

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

我对这两个选项的考虑:

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

4 回答 4

15

我发现还有一种方法可以解决这个问题!我的同事节省了我一天的时间,将我指向这个博客,上面写着:

当已经在 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();
}
于 2010-08-25T14:11:53.993 回答
3

就我而言,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)
{
}
于 2013-07-08T09:25:17.420 回答
1

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

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

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

-Oisin

于 2010-08-24T17:52:13.343 回答
1

奇科多罗是对的。

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;
        }
    }
于 2012-05-24T14:23:29.013 回答