2013-05-31 3 views
0

Я использую Oracle ODAC.NET для проекта .NET 3.5 против базы данных Oracle 11 Express, и я вижу поведение, которое я не могу объяснить (и, похоже, не работает).ODAC, похоже, кэширует схему таблицы?

ODAC должен быть последним, я просто вытащил его 3 дня назад, но версии таковы:

  • Oracle.DataAccess.dll версия 2.112.3.0 (выпуск 5)
  • oci.dll (клиент мгновенного) версии 11.2.0.1

у меня есть таблица, люди, что имеет 3 колонки:

  • ID
  • FirstName
  • LastName

В коде я запускаю команду ALTER TABLE, используя OracleCommand.ExecuteNonQuery, чтобы добавить новый столбец с именем "MIDDLE_NAME" к столу. Эта команда преуспевает. Если я посмотрю на таблицу с Oracle SQL Developer, появятся столбцы. Все хорошо и хорошо.

Теперь, если я бегу использовать 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
  • Я использую тот же OracleConnection для SELECT и для ALTER
  • Я использую новый OracleCommand для SELECT чем для ALTER
  • я использую тот же OracleCommand для SELECT и для ALTER
  • Я называю PurgeStatementCache на связи между ALTER и SELECT
  • Я называю FlushCache на связи между ALTER и SELECT
  • я явно Close и DisposeOracleCommand и OracleConnection (в отличие от, используя блок), используемые для ALTER и SELECT
  • перезапущены вызывающие ПК и ПК хостинга базы данных Oracle.

Если я смотрю на список столбцов, выполнив SELECT * FROM all_tab_cols, там будет новый столбец.

Единственное, что работает надёжно, - это закрыть приложение и перезапустить его (ну, это из единичного теста, но это остановка и перезапуск тестового узла). Затем я получаю эту четвертую колонку. Иногда я могу использовать точки останова и повторно выполнять запросы, и появится 4-й столбец, но ничего, что можно повторить с прямым выполнением кода (что означает без установки точки останова и перемещения точки выполнения).

Что-то в недрах ODAC, похоже, кэширует схему этого стола, но я могу выяснить, что, почему и как предотвратить это. У кого-нибудь есть опыт с этим или идеи, как я могу это предотвратить?

+0

Вы пробовали оформив "Commit" заявление в SQL Developer? Если вы не вносите явные изменения в фиксацию, обновления, вставки и ddl не будут сохраняться, они просто сидят в открытой транзакции, так как SQL Developer обертывает их по умолчанию. Это противоположное поведение SQL Server, где вы должны определить транзакцию перед фиксацией. –

+0

Просто попробовал. Положите точку разрыва между ALTER и SELECT, побежали к этому перерыву, сделали COMMIT в SQL Developer, затем запустили SELECT, и я все еще возвращаю только 3 столбца. – ctacke

+0

@DavidC вам не нужно вызывать фиксацию после выполнения таблицы alter (ее неявной), но, похоже, что-то странное. И я не знаю, почему этот вопрос получил downvote, кажется лучше, чем многие, которые я вижу на вкладках Oracle.+1 – tbone

ответ

1

Возможно, вы найдете некоторые из ваших C# -кодов. Ниже приведен тест, который ведет себя как ожидалось, то есть я могу увидеть новый столбец сразу после его добавления. Это использование ODP 11.2 отн 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 

Edit: А, ок, я понимаю, что вы говорите, это похоже на кэширование операторов. Я играл с изменением размера кеша до 0 (в строке подключения, используйте «Размер кэша Statement = 0»), а также попытался cmd.AddToStatementCache = false, но они не сработали.

Одна вещь, которая работает, заключается в использовании немного другой строки, например, при добавлении пробела. Я знаю его взломать, но это все, что я могу заставить работать для меня в любом случае.

Попробуйте пример с:

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

И использовать SQL3 перед добавлением столбца и sql5 после добавления столбца.

Надежда, что помогает

+0

Да, то, что у вас здесь, будет работать. Однако выполнение SELECT * из T1 не даст вам нового поля, и это проблема. Я добавил свой тестовый код в вопрос. – ctacke

+0

@ctacke updated answer – tbone

+0

К сожалению, само утверждение является тем, что я сгенерировал, и я действительно не знаю, было ли это сделано раньше (я мог бы добавить хакерство, чтобы считать это, я думаю). Это часть большего ORM, который у меня есть, который поддерживает несколько баз данных и ОС, поэтому я стараюсь избегать слишком многих конкретных хаков. – ctacke

1

Я знаю, что этот ответ приходит годами позже, но если новые читатели столкнулись с проблемами при настройке кэширования попробовать:

Metadata Pooling = ложь, Self Tuning = False и себе Cache Size = 0

... в строке подключения. Имейте в виду, что для этого есть последствия для производительности.

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

Смежные вопросы