ODAC似乎在缓存表模式

本文关键字:模式 缓存 ODAC | 更新日期: 2023-09-27 18:16:39

我正在使用Oracle的ODAC。.NET的。NET 3.5项目对Oracle 11 Express数据库,我看到的行为,我无法解释(似乎无法解决)。

ODAC应该是最新的,我3天前才拉出来的,但是版本如下:

    Oracle.DataAccess.dll version 2.112.3.0 (release 5)oci.dll(即时客户端)version 11.2.0.1

我有一个表,People,它有3列:

  • ID
  • FirstName
  • LastName

在代码中,我使用OracleCommand.ExecuteNonQuery运行ALTER TABLE命令,向表中添加一个名为"MIDDLE_NAME"的新列。命令执行成功。如果我用Oracle SQL Developer查看表,列就会显示出来。一切都好。

现在,如果我运行使用OracleCommand.ExecuteReaderSELECT * FROM People的命令文本后,我做alter表,我得到的数据与只有3列,而不是4!

下面是重现这个问题的代码:

public void FieldTest()
{
    var sql1 = "CREATE TABLE People (" +
        "ID NUMBER PRIMARY KEY, " +
        "FirstName NVARCHAR2 (200), " +
        "LastName NVARCHAR2 (200) NOT NULL)";
    var sql2 = "ALTER TABLE People " +
        "ADD Middle_Name NUMBER";
    var sql3 = "SELECT * FROM People";
    var sql4 = "SELECT column_name FROM all_tab_cols WHERE table_name = 'PEOPLE'";
    var cnInfo = new OracleConnectionInfo("192.168.10.246", 1521, "XE", "system", "password");
    var connectionString = BuildConnectionString(cnInfo);
    using (var connection = new OracleConnection(connectionString))
    {
        connection.Open();
        using (var create = new OracleCommand(sql1, connection))
        {
            create.ExecuteNonQuery();
        }
        using (var get = new OracleCommand(sql3, connection))
        {
            using (var reader = get.ExecuteReader())
            {
                Debug.WriteLine("Columns: " + reader.FieldCount);
                // outputs 3, which is right
            }
        }
        using (var alter = new OracleCommand(sql2, connection))
        {
            alter.ExecuteNonQuery();
        }
        using (var get = new OracleCommand(sql3, connection))
        {
            using (var reader = get.ExecuteReader())
            {
                Debug.WriteLine("Columns: " + reader.FieldCount);
                // outputs 3, which is *wrong* <---- Here's the problem
            }
        }
        using (var cols = new OracleCommand(sql4, connection))
        {
            using (var reader = cols.ExecuteReader())
            {
                int count = 0;
                while (reader.Read())
                {
                    count++;
                    Debug.WriteLine("Col: " + reader.GetString(0));
                }
                Debug.WriteLine("Columns: " + count.ToString());
                // outputs 4, which is right
            }
        }
    }
}

我尝试了一些方法来防止这种行为,但是没有一个返回给我第4列:

    我关闭连接并重新打开它
  • 我为SELECT使用了一个新的OracleConnection,而不是ALTER
  • 我对SELECTALTER使用相同的OracleConnection
  • 我为SELECT使用新的OracleCommand,而不是ALTER
  • 我对SELECTALTER使用相同的OracleCommand
  • 我在ALTERSELECT之间的连接上调用PurgeStatementCache
  • ALTERSELECT之间的连接上调用FlushCache
  • 我明确CloseDisposeOracleCommandOracleConnection(相对于使用块)用于ALTERSELECT
  • 重启调用PC和Oracle数据库所在PC。

如果我通过SELECT * FROM all_tab_cols查看列列表,新列就在那里。

唯一可靠的工作似乎是关闭应用程序并重新启动它(好吧,这是从一个单元测试,但它是一个关闭和重新启动测试主机)。然后得到第四列。有时我可以使用断点并重新执行查询,并且会出现第4列,但是直接执行代码(意思是不设置断点并将执行点移回)没有任何特别可重复的内容。

ODAC内部的某些东西似乎正在缓存该表的模式,但我可以弄清楚是什么,为什么或者如何防止它。有人有过这样的经历吗,或者知道我该如何预防吗?

ODAC似乎在缓存表模式

我知道这个答案要过几年才会出现,但是如果新读者遇到缓存问题,请尝试设置:

元数据池= false,自调优= false,语句缓存大小= 0

…在连接字符串中。请记住,这样做会影响性能。

https://docs.oracle.com/database/122/ODPNT/featConnecting.htm guid - 0 - cfeb161 - 68 ef - 4 - bc2 - 8943 - 3 - bdffb878602

也许可以发布一些你的c#代码。下面是一个按照预期运行的测试,这意味着我可以在添加新列后立即看到它。这是使用odp 11.2 rel 5访问11g数据库,使用4.0框架:

测试表为:

CREATE TABLE T1
(
  DTE  DATE default sysdate
);

在每次运行以下c#代码后,删除并重新创建它(有点脏,但无论如何):

string connStr = "User Id=xxx;Password=yyy;Data Source=my11gDb;";
using (OracleConnection con = new OracleConnection(connStr))
{
    string s = "ALTER TABLE T1 ADD (added_col VARCHAR2(10))";
    using (OracleCommand cmd = new OracleCommand(s, con))
    {
        con.Open();
        cmd.ExecuteNonQuery();
        string s2 = "select column_name from all_tab_columns where table_name = 'T1'";
        //con.FlushCache(); // doesn't seem to matter, works with or without
        using (OracleCommand cmd2 = new OracleCommand(s2, con))
        {
            OracleDataReader rdr = cmd2.ExecuteReader();
            for (int i = 0; rdr.Read(); i++)
            {
                Console.WriteLine("Column {0} => {1}",i+1,rdr.GetString(0));
            }
            rdr.Close();
        }
    }
}
输出:

Column 1 => DTE
Column 2 => ADDED_COL
编辑:

啊,好的,我明白你的意思了,它看起来像语句缓存。我玩了改变缓存大小为0(在conn字符串中,使用"语句缓存大小=0"),也尝试了cmd。AddToStatementCache = false,但这些都没有工作

可以使用稍微不同的字符串,比如添加一个空格。我知道这是一个hack,但这是我能得到的所有为我工作。

试试你的例子:

var sql3 = "SELECT * FROM People";
var sql5 = "SELECT * FROM People "; // note extra space

并且在添加列之前使用sql3,在添加列之后使用sql5。

希望有帮助