ODP.NET:参数类型在相同的CommandText上“缓存”

时间:2013-07-30 11:19:06

标签: c# ado.net odp.net

我正在评估Oracle的ODP.NET DataProvider,我遇到了一个问题,它出现在我们的一个测试用例中:当使用不同的参数类型执行相同的命令文本时,使用第一个执行命令的参数类型在以下所有命令中。

以下面的代码为例:

const int sampleInt32 = 1234567890;
const string sampleNvarchar = "someTestString";

const string sqlCommandtext = "SELECT :PARAM PARAM FROM DUAL";
using (OracleConnection connection = new OracleConnection(builder.ConnectionString))
{
    connection.Open();

    //Test 1 - Int 32
    using (OracleCommand commandInt32 = connection.CreateCommand())
    {
        commandInt32.CommandText = sqlCommandtext;
        commandInt32.Parameters.Add("PARAM", OracleDbType.Int32, sampleInt32, ParameterDirection.Input);
        using (IDataReader reader = commandInt32.ExecuteReader())
        {
            while (reader.Read())
            {
                int resultInt32 = (int)reader.GetDecimal(0);
                Assert.AreEqual(sampleInt32, resultInt32);
            }
        }
    }
    //Test 2 - NVarchar
    using (OracleCommand commandNVarchar = connection.CreateCommand())
    {
        commandNVarchar.CommandText = sqlCommandtext;
        commandNVarchar.Parameters.Add("PARAM", OracleDbType.NVarchar2, sampleNvarchar, ParameterDirection.Input);
        using (IDataReader reader = commandNVarchar.ExecuteReader())
        {
            while (reader.Read())
            {
                string resultNVarchar = reader.GetString(0);
                Assert.AreEqual(sampleNvarchar, resultNVarchar);
            }
        }
    }
}

如果在commandNVarchar之前执行commandInt32,则commandNVarchar的执行失败,并带有ORA-01722 - 无效的数字。如果订单被切换,那么首先执行commandNVarchar,它会在reader.GetDecimal上失败并显示“Specified cast is valid”。

到目前为止,我已尝试设置StatementCacheSize = 0;池= FALSE; StatementCachePurge = true作为ConnectionString参数,但我无法使其工作。

我有什么遗漏或者还有其他值得尝试的选择吗?

编辑:也许有一些背景知道为什么需要/需要:我们不直接在我们的应用程序中使用ODP或任何其他Dataprovider(或者至少:我们正在前往达成这个目标),介于两者之间的DataLayer执行数据库/提供程序特定的优化和监视连接健康状况,...

在此图层中,例如可以调用StoredProcedures,可以选择参数类型调整。我们的一些程序将Clobs作为参数类型,因为有时值可能长于x个字符,但很可能它会更短。 因此,在通过将ArrayBindCount设置为y的ExecuteNonQuery执行之前,如果Clob可以作为varchar(Nclob作为Nvarchar)传递,则检查参数值。 “重新绑定”将执行2500条记录的时间从大约500毫秒减少到200毫秒,代价是丢失几毫秒检查字符串长度。只有在可以更改参数类型时才能进行重新绑定。如果没有这个选项,我们每次都需要以Clob的形式执行它,从而降低性能。

4 个答案:

答案 0 :(得分:3)

据我了解,SELECT列表中不支持参数绑定。我很惊讶这一点,我必须运行你的代码才能亲眼看到它。我相信客户端允许执行SQL语句是一个错误。

无论如何,我在测试用例之间插入了以下行以使它们都起作用:

connection.PurgeStatementCache();

但是,这似乎只适用于托管客户端(我已经尝试使用版本4.112.3.60)。常规客户端仍然会失败,如您所述。

答案 1 :(得分:2)

两件事。当用作连接字符串参数时,配置变量需要有空格,即

Statement Cache Size=0;

您使用的格式可以直接在配置中使用: http://docs.oracle.com/html/E10927_01/featConfig.htm#CJABCACG

                 

http://docs.oracle.com/html/E10927_01/featOraCommand.htm#CIHCAFIG

您可以使用相同的配置部分来启用跟踪 - 比较跟踪可能会让您了解正在发生的事情。

我相信PurgeStatementCache(不确定StatementCachePurge是否存在)是一个运行时命令,即

connection.PurgeStatementCache

答案 2 :(得分:1)

您要连接到哪个版本的Oracle?这可能是绑定变量峰值(或缺少)问题。该功能是在9i中引入的,但是从10到10都有一些问题。您可以尝试执行以下操作,看看是否可以在没有ODP.net的情况下重现问题:

var param varchar2(255)
exec :param:='TEST';
select :param FROM DUAL;

将“param”上的类型从varchar2更改为number并更改该值并重新执行以查看发生的情况。

您也可以尝试在不同的连接下执行命令,而不是共享连接。

最后,您可以简单地在语句中重命名绑定变量,相对于类型(即:paramNum或:paramString)。除非将cmd.BindByName设置为true,否则在.net端给出参数的名称是无关紧要的。默认情况下,它为false,变量按添加顺序绑定。

答案 3 :(得分:1)

Metadata Pooling = false;

我们的应用程序使用Oracle 12c和ODP.Net托管提供程序

使用OracleCommandBuilder.DeriveParameters()时,尽管添加/删除/更新参数,我们总是看到从存储过程返回相同的参数。我们只会在重新启动IIS进程后看到更改。

唯一有效的解决方案是在Oracle连接字符串

中设置Metadata Pooling = false;

我们在此处或Oracle论坛上提及的以下内容均未取得成功:

connection.PurgeStatementCache();

Statement Cache Size=0;

Pooling = false;