ODAC, кажется, кеширует схему таблицы?
Я использую ODAC.NET от Oracle для проекта.NET 3.5 с базой данных Oracle 11 Express, и я вижу поведение, которое не могу объяснить (и, похоже, не могу обойти).
ODAC должен быть последним, я только что вытащил его 3 дня назад, но версии следующие:
- Oracle.DataAccess.dll версия 2.112.3.0 (выпуск 5)
- oci.dll (мгновенный клиент) версия 11.2.0.1
У меня есть таблица, Люди, которая имеет 3 столбца:
- Я БЫ
- Имя
- Фамилия
В коде я запускаю команду 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
а такжеDispose
OracleCommand
а такжеOracleConnection
(в отличие от использования блока) используется дляALTER
а такжеSELECT
- Перезапустил вызывающий ПК и ПК, на котором размещена база данных Oracle.
Если я посмотрю на список столбцов, выполнив SELECT * FROM all_tab_cols
новый столбец есть.
Единственная вещь, которая, кажется, работает надежно - это закрытие приложения и его повторный запуск (ну, это из модульного теста, но это завершение работы и перезапуск тестового хоста). Тогда я получаю этот 4-й столбец. Иногда я могу использовать точки останова и повторно выполнять запросы, и появляется 4-й столбец, но ничего, что было бы специально повторяемым при прямом выполнении кода (то есть без установки точки останова и перемещения точки выполнения обратно вверх).
Кажется, что-то в недрах ODAC кеширует схему этой таблицы, но я могу понять, что, почему или как это предотвратить. У кого-нибудь есть опыт или идеи, как я могу это предотвратить?
2 ответа
Я знаю, что этот ответ приходит спустя годы, но если у новых читателей возникнут проблемы с кэшированием, попробуйте установить:
Пул метаданных = ложь, самонастройка = ложь и размер кэша операторов = 0
... в строке подключения. Имейте в виду, что это влияет на производительность.
https://docs.oracle.com/database/122/ODPNT/featConnecting.htm
Может быть, опубликовать некоторые из вашего кода C#. Ниже приведен тест, который ведет себя так, как ожидалось. Это означает, что я могу видеть новый столбец сразу после его добавления. Это использует odp 11.2 rel 5, достигнув 11g db, используя 4.0 framework:
Тестовая таблица:
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, но это не сработало.
Одна вещь, которая действительно работает, - это использование немного другой строки, например, добавление пробела. Я знаю, что это хак, но это все, что я могу заставить меня работать.
Попробуйте свой пример с:
var sql3 = "SELECT * FROM People";
var sql5 = "SELECT * FROM People "; // note extra space
И используйте sql3 перед добавлением столбца, и sql5 после добавления столбца.
надеюсь, это поможет