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

时间:2010-08-24 15:15:47

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

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

我需要从C#Web服务调用存储过程(Oracle 10g数据库)。 Web服务器安装了Oracle 9i客户端,我正在使用Microsofts 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例程方面也需要付出相当大的努力。

4 个答案:

答案 0 :(得分:13)

我发现另一种解决问题的方法!我的同事救了我的一天,指着我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();
}

答案 1 :(得分: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)
{
}

答案 2 :(得分: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;
        }
    }

答案 3 :(得分:0)

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

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

基本上,您不能在字符串文字中使用超过4000个字符,如果您需要执行更多操作,则必须使用存储过程。然后,您最多限制为32KB,因此您必须“插入”插入。布莱什。

-Oisin