ODAC似乎是缓存表模式?

时间:2013-05-31 14:21:16

标签: c# .net oracle odac

我正在使用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

我有一个表,人物,有3列:

  • ID
  • 名字

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

现在,如果我在执行alter table后立即使用命令文本OracleCommand.ExecuteReader运行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
            }
        }
    }
}

我已经尝试了一些防止这种行为的事情,但没有一个能让我回到第4栏:

  • 我关闭连接并重新打开
  • 我为OracleConnection使用新SELECT而不是ALTER
  • 我对OracleConnectionSELECT
  • 使用相同的ALTER
  • 我为OracleCommand使用新SELECT而不是ALTER
  • 我对OracleCommandSELECT
  • 使用相同的ALTER
  • 我在PurgeStatementCacheALTER
  • 之间的联系上致电SELECT
  • 我在FlushCacheALTER
  • 之间的联系上致电SELECT
  • 我明确CloseDispose OracleCommandOracleConnection(与使用块相对)用于ALTERSELECT
  • 重新启动了主叫PC和托管Oracle数据库的PC。

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

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

ODAC的内容似乎正在缓存该表的架构,但我可以弄清楚是什么,为什么或如何防止它。任何人都有这方面的经验,或者我是如何预防它的想法?

2 个答案:

答案 0 :(得分:1)

也许发布一些C#代码。以下是一个按预期运行的测试,这意味着我可以在添加后立即看到新列。这是使用odp 11.2 rel 5使用4.0框架命中11g db:

测试表是:

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,但这些都不起作用。

有效的方法是使用稍微不同的字符串,例如添加空格。我知道这是一个黑客,但无论如何,这就是我可以为我工作的全部。

尝试使用以下示例:

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

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

希望有所帮助

答案 1 :(得分:1)

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

Metadata Pooling = false,Self Tuning = False,Statement Cache Size = 0

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

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