ODAC seems to be caching table schema?

爷,独闯天下 提交于 2019-12-23 02:47:12

问题


I'm using Oracle's ODAC.NET for a .NET 3.5 project against an Oracle 11 Express database, and I'm seeing behavior that I can't explain (and can't seem to work around).

ODAC should be the latest, I just pulled it 3 days ago, but the versions are as follows:

  • Oracle.DataAccess.dll version 2.112.3.0 (release 5)
  • oci.dll (instant client) version 11.2.0.1

I have a Table, People, that has 3 columns:

  • ID
  • FirstName
  • LastName

In code I run an ALTER TABLE command, using OracleCommand.ExecuteNonQuery, to add a new column named "MIDDLE_NAME" to the table. That command succeeds. If I look at the table with Oracle SQL Developer, the columns shows up. All well and good.

Now if I run use OracleCommand.ExecuteReader with a command text of SELECT * FROM People right after I do the alter table, I get back data with only 3 columns, not 4!

Here is code that reproduces the problem:

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
            }
        }
    }
}

I've tried some things to prevent the behavior, and none of them give me back the 4th column:

  • I close the connection and re-open it
  • I use a new OracleConnection for the SELECT than for the ALTER
  • I use the same OracleConnection for the SELECT and for the ALTER
  • I use a new OracleCommand for the SELECT than for the ALTER
  • I use the same OracleCommand for the SELECT and for the ALTER
  • I call PurgeStatementCache on the connection between the ALTER and SELECT
  • I call FlushCache on the connection between the ALTER and SELECT
  • I explicitly Close and Dispose the OracleCommand and OracleConnection (as opposed to the using block) used for the ALTER and SELECT
  • Restarted the calling PC and the PC hosting the Oracle database.

If I look at the column list by doing a SELECT * FROM all_tab_cols, the new column is there.

The only thing that seems to work reliably is closing the app and re-starting it (well it's from a unit test, but it's a shutdown and restart of the test host). Then I get that 4th column. Sometimes I can use breakpoints and re-execute queries and the 4th column will appear, but nothing that is specifically repeatable with straight execution of code (meaning without setting a break point and moving the execution point back up).

Something in the bowels of ODAC seems to be caching the schema of that table, but I can figure out what, why or how to prevent it. Anyone have any experience with this, or ideas how I might prevent it?


回答1:


Maybe post some of your C# code. The following is a test that behaves as expected, meaning I can see the new column immediately after adding it. This is using odp 11.2 rel 5 hitting an 11g db, using 4.0 framework:

The test table is:

CREATE TABLE T1
(
  DTE  DATE default sysdate
);

Drop and recreate it after each run of the following C# code (a bit dirty but anyway):

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();
        }
    }
}

Output:

Column 1 => DTE
Column 2 => ADDED_COL

Edit: Ah, ok, I see what you're saying, it looks like statement caching. I played around with changing the cache size to 0 (in conn string, use "Statement Cache Size=0"), and also tried cmd.AddToStatementCache = false, but these did not work.

One thing that does work is to use a slightly different string, like adding a space. I know its a hack, but this is all I can get to work for me anyway.

Try your example with:

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

And use sql3 before adding column, and sql5 after adding a column.

Hope that helps




回答2:


I know this answer comes years later but if new readers run into problems with caching try setting:

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

...in the connection string. Keep in mind that there are performance implications for doing so.

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



来源:https://stackoverflow.com/questions/16859567/odac-seems-to-be-caching-table-schema

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!