1

我将 Oracle 的 ODAC.NET 用于针对 Oracle 11 Express 数据库的 .NET 3.5 项目,并且我看到了我无法解释的行为(而且似乎无法解决)。

ODAC应该是最新的,我3天前刚拉的,不过版本如下:

  • Oracle.DataAccess.dll 版本 2.112.3.0(第 5 版)
  • oci.dll(即时客户端)版本 11.2.0.1

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

  • ID

在代码中,我运行 ALTER TABLE 命令,使用OracleCommand.ExecuteNonQuery, 将名为“MIDDLE_NAME”的新列添加到表中。该命令成功。如果我使用 Oracle SQL Developer 查看表格,就会显示列。一切都很好。

现在,如果我在执行 alter table 后立即使用OracleCommand.ExecuteReader命令文本运行 use SELECT * FROM People,我将返回只有 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
            }
        }
    }
}

我尝试了一些方法来防止这种行为,但它们都没有给我第四列:

  • 我关闭连接并重新打开它
  • 我用 newOracleConnection代替SELECTALTER
  • OracleConnection对 theSELECT和 for the使用相同的ALTER
  • 我用 newOracleCommand代替SELECTALTER
  • OracleCommand对 theSELECT和 for the使用相同的ALTER
  • 我呼吁和PurgeStatementCache之间的联系ALTERSELECT
  • 我呼吁和FlushCache之间的联系ALTERSELECT
  • 我明确地CloseDisposeand OracleCommandOracleConnection与 using 块相反)用于ALTERandSELECT
  • 重新启动调用 PC 和托管 Oracle 数据库的 PC。

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

唯一似乎可靠工作的是关闭应用程序并重新启动它(它来自单元测试,但它是测试主机的关闭和重新启动)。然后我得到第四列。有时我可以使用断点并重新执行查询,并且会出现第 4 列,但没有任何东西可以通过直接执行代码来特别重复(意味着没有设置断点并将执行点向上移动)。

ODAC 内部的某些东西似乎正在缓存该表的模式,但我可以弄清楚是什么、为什么或如何阻止它。任何人都有这方面的经验,或者想法我可以如何防止它?

4

2 回答 2

2

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

元数据池 = 假,自我调整 = 假,语句缓存大小 = 0

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

https://docs.oracle.com/database/122/ODPNT/featConnecting.htm#GUID-0CFEB161-68EF-4BC2-8943-3BDFFB878602

于 2017-01-25T22:45:45.050 回答
1

也许发布一些你的 C# 代码。以下是按预期运行的测试,这意味着我可以在添加新列后立即看到它。这是使用 odp 11.2 rel 5 达到 11g db,使用 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 字符串中,使用“Statement Cache Size=0”),还尝试了 cmd.AddToStatementCache = false,但这些都不起作用。

有效的一件事是使用稍微不同的字符串,例如添加空格。我知道这是一个 hack,但无论如何,这就是我能为我工作的全部。

试试你的例子:

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

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

希望有帮助

于 2013-05-31T15:58:26.960 回答