#c# #.net #oracle #ado.net #odp.net
#c# #.net #Oracle #ado.net #odp.net
Вопрос:
Я делаю следующее:
protected int CreateComponent(DbConnection cnctn, string tableName)
{
int newId;
DbCommand selectCmd = _provFactory.CreateCommand();
selectCmd.Connection = cnctn;
selectCmd.CommandText = string.Format(
"SELECT * FROM {0} WHERE ID = (SELECT MAX(ID) FROM {0})", tableName);
DbDataAdapter dataAdapter = _provFactory.CreateDataAdapter();
dataAdapter.SelectCommand = selectCmd;
...
// create Insert/Update/Delete commands with a builder for the data adapter
...
dataAdapter.Fill(_dataSet, tableName);
newId = Convert.ToInt32(_dataSet.Tables[tableName].Rows[0]["id"]) 1000000;
DataRow newRow = _dataSet.Tables[tableName].NewRow();
newRow.ItemArray = _dataSet.Tables[tableName].Rows[0].ItemArray;
newRow["ID"] = newId;
_dataSet.Tables[tableName].Rows.Add(newRow);
}
Это отлично работает для OleDb и System.Data.OracleClient. Однако с Oracle.Доступ к данным.Поставщик клиента, которого я получаю:
Oracle.DataAccess.Types.OracleTruncateException (16550)
с усеченным текстом результата, полученного из:
at System.Data.Common.DbDataAdapter.UpdatedRowStatusErrors
at System.Data.Common.DbDataAdapter.UpdatedRowStatus
at System.Data.Common.DbDataAdapter.Update
at Oracle.DataAccess.Client.OracleDataAdapter.Update
at System.Data.Common.DbDataAdapter.UpdateFromDataTable
at System.Data.Common.DbDataAdapter.Update
Таблицы, которые я получаю, это большие таблицы, которые содержат 61 поле. Типы всех полей ограничены:
VARCHAR2(different lenghts)
VARCHAR2(different lenghts) NOT NULL
FLOAT(126) NOT NULL
NUMBER NOT NULL
DATE
Отредактируйте, чтобы предотвратить слишком много комментариев:
-Я не могу изменить тип данных или что-либо еще в базе данных.
-В DataRow эти столбцы с плавающей точкой (126) имеют тип данных System.Десятичный (как при использовании других поставщиков)
-В отличие от того, что я говорил ранее: ID не является первичным ключом. Это уникальный индекс. Таблица не имеет первичного ключа (как определение Oracle) Я должен признать, что я думал, что уникальный индекс является первичным ключом, что может показаться нелепым для людей, знакомых с Oracle. В любом случае я делаю вставку только из 1 строки. Я не пытался вручную создать Insert-command, что я сделаю немного позже. Разработчики команд должны обрабатывать таблицы без PK (http://msdn.microsoft.com/en-us/library/tf579hcz.aspx : «Команда SelectCommand также должна возвращать хотя бы один первичный ключ или уникальный столбец».)
-Это работает также с ODP.NET/Oracle.Доступ к данным.Клиент, если:
- Я присваиваю всем столбцам с плавающей точкой (126) значение 0 перед последней строкой метода. Даже при присвоении значения 1 или 2 любому возникает такое же исключение, когда DbDataAdapter.Вызывается обновление.
или
- Я создаю DbDataAdapter.Insertommand сам, и есть только вставка (как код выше), когда DbDataAdapter.Вызывается обновление. Когда я сам создаю cmd, я даю DbParameter.DbType = DbType.Дважды для столбцов с плавающей точкой (126). Если я создаю его сам, принимаются все обычные двойные значения.
app.config:
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup>
<system.data>
<DbProviderFactories>
<add name="Oracle Data Provider for .NET"
invariant="Oracle.DataAccess.Client"
description="Oracle Data Provider for .NET"
type="Oracle.DataAccess.Client.OracleClientFactory,
Oracle.DataAccess,
Version=2.112.1.0,
Culture=neutral,
PublicKeyToken=89b483f429c47342" />
</DbProviderFactories>
</system.data>
есть идеи, в чем причина и как я собираюсь заставить это работать для всех 3 поставщиков?
Спасибо и с наилучшими пожеланиями — Matti
Комментарии:
1. Из моего опыта ODP.NET имеет какое-то странное поведение, а иногда и странные ошибки в зависимости от того, какую версию вы используете и какой клиент Oracle вы используете и т.д. К сожалению, на данный момент
System.Data.OracleClient
это устарело… Эти вещи приводят меня к использованию коммерческого провайдера… довольно рад, что пока одна ошибка (которая была исправлена немедленно) и никогда не оглядывалась назад…. Хотя я не знаю, подходит ли вам этот вариант…2. Имеет ли таблица, показывающая это поведение, первичный ключ?
3. да, это «ID». я дополнительно протестировал его и извлек строку maxid, как указано выше, но затем изменил значения новой строки из 1-го столбца с плавающей точкой (126) на последний столбец (NUMBERs и FLOAT (126) s получают 0, VARCHAR2s получают короткую бессмысленную строку, а DATEs получают DateTime. Теперь) и проблем нет! когда я оставляю 1-е поле с плавающей запятой таким, каким оно было, возникает исключение, поэтому оно не допускает значений, которые извлекаются из базы данных!!!
4. Я только что заметил
FLOAT (126)
— это, конечно, странно, особенно. в базе данных Oracle … вы можете уточнить?5. попробуйте использовать
NUMBER
вместо этого — это гораздо более «естественно» для базы данных Oracld и имеет немного более высокую точность (38 вместо 37,9).
Ответ №1:
Во-первых, я думаю, вам следует сообщить об этом Oracle как об ошибке. Ошибка возникает, даже если таблица действительно маленькая. Это не имеет ничего общего с индексами или первичными ключами, ошибка возникает, даже если таблица не имеет индекса. Если вы установите значение ID равным 0, вставка будет выполнена нормально.
Мне удалось создать обходной путь, хотя он и не очень хороший, но может быть достаточно хорошим для вашего случая.
Обходной путь заключается в использовании собственных клиентских классов Oracle для ODP.Net поэтому вам придется проверить, было ли ваше приложение настроено для ODP или одного из других, и соответствующим образом выбрать свой код.
Версия вашей функции «Только для ODP» может выглядеть следующим образом:
protected void CreateComponentODPOnly(Oracle.DataAccess.Client.OracleConnection cntn, string tableName)
{
int newId;
System.Data.DataSet _dataSet = new DataSet();
Oracle.DataAccess.Client.OracleCommand selectCmd = new Oracle.DataAccess.Client.OracleCommand();
selectCmd.Connection = cntn;
selectCmd.CommandText = string.Format(
"SELECT * FROM {0} WHERE ID = (SELECT MAX(ID) FROM {0})", tableName);
Oracle.DataAccess.Client.OracleDataAdapter dataAdapter = new Oracle.DataAccess.Client.OracleDataAdapter();
Oracle.DataAccess.Client.OracleCommandBuilder cmdBuilder = new Oracle.DataAccess.Client.OracleCommandBuilder();
dataAdapter.SelectCommand = selectCmd;
cmdBuilder.DataAdapter = dataAdapter;
dataAdapter.Fill(_dataSet, tableName);
newId = Convert.ToInt32(_dataSet.Tables[tableName].Rows[0]["id"]) 1000000;
DataRow newRow = _dataSet.Tables[tableName].NewRow();
newRow.ItemArray = _dataSet.Tables[tableName].Rows[0].ItemArray;
newRow["ID"] = (Decimal)newId;
_dataSet.Tables[tableName].Rows.Add(newRow);
dataAdapter.InsertCommand = cmdBuilder.GetInsertCommand();
dataAdapter.Update(_dataSet.Tables[tableName]);
}
Комментарии:
1. Спасибо GTG! Я также сам нашел обходной путь, который я отредактировал в вопросе (создавая сам). Ваш обходной путь достаточно хорош, если он работает! Код уже полон условий, проверяющих поставщика, потому что параметры, схемы и т.д. Обрабатываются по-разному. Я попытаюсь протестировать это сегодня и определенно соглашусь, если это сработает. Еще раз спасибо!
2. еще раз спасибо! это работает, что странно, потому что адаптер данных и конструкторы являются экземплярами OracleDataAdapter и OracleCommandBuilder. есть какие-либо объяснения? Я еще немного протестирую, прежде чем принять.
3. Я провел еще несколько тестов и обнаружил, что проблема, похоже, в commandbuilder. GetInsertCommand() . Мой тест заключался в использовании _provFactory . Создать… для всех объектов и для использования системы. Типы данных данных. Это отлично работает, если вы делаете это следующим образом: DataAdapter. InsertCommand = ((Oracle. Доступ к данным. Клиент. OracleCommandBuilder)cmdBuilder). GetInsertCommand(); Это кажется странным — можно было бы предположить, что это виртуальная функция, поэтому это приведение к типу не должно иметь никакого эффекта.
4. действительно странно. в любом случае проблема «решена». действительно странно, что ошибка в этой обычной операции остается незамеченной (согласно моим поискам в Интернете). возможно, никто на самом деле не использует ODP.NET потому что там полно ошибок.
Ответ №2:
Вы пробовали добавлять PK после заполнения таблицы?
_dataSet.Tables[tableName].PrimaryKey = (new List<DataColumn>() { _dataSet.Tables[0].Columns["ID"] }).ToArray();
Комментарии:
1. что такое CurrentDataSet . Таблицы [0]? но если вы имеете в виду попытку добавить столбец ID как PK, я этого не сделал. Я пытаюсь это сделать.
2. добавление «ID» в качестве PK не имело никакого эффекта : (
3. извините, это должно было быть _dataSet . Я обновил свой ответ, хотя, похоже, вы уже поняли это и попробовали это.