为啥我在调用 DbDataAdapter.Update 时使用 ODP.NET OracleDataAdapter 而不是使用 System.Data.OracleClient 的适配器得到 Orac
Posted
技术标签:
【中文标题】为啥我在调用 DbDataAdapter.Update 时使用 ODP.NET OracleDataAdapter 而不是使用 System.Data.OracleClient 的适配器得到 OracleTruncateException?【英文标题】:why I get OracleTruncateException with ODP.NET OracleDataAdapter but not with System.Data.OracleClient's adapter when DbDataAdapter.Update called?为什么我在调用 DbDataAdapter.Update 时使用 ODP.NET OracleDataAdapter 而不是使用 System.Data.OracleClient 的适配器得到 OracleTruncateException? 【发布时间】:2011-11-11 14:05:55 【问题描述】:我执行以下操作:
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.DataAccess.Client 的提供程序,我得到:
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
编辑以防止过多的 cmets:
-我无法更改数据库中的数据类型或任何内容。
-在 DataRow 中,这些 FLOAT(126) 列的数据类型为 System.Decimal(就像使用其他提供程序时一样)
-与我之前所说的不同:ID 不是主键。这是唯一的索引。表没有主键(如 Oracle 定义) 我不得不承认,我认为唯一索引是主键,这对于熟悉 Oracle 的人来说可能听起来很荒谬。无论如何,我只插入 1 行。我还没有尝试手动构建插入命令,我稍后会做。命令构建器应处理没有 PK 的表(http://msdn.microsoft.com/en-us/library/tf579hcz.aspx:“SelectCommand 还必须返回至少一个主键或唯一列。”)
-这也适用于 ODP.NET/Oracle.DataAccess.Client 如果:
我在方法的最后一个之前给所有 FLOAT(126)-columns 值 0 排。即使将值 1 或 2 赋予 any 也会引发相同的异常 调用 DbDataAdapter.Update。或
我自己创建了 DbDataAdapter.Insertommand 并且只有 调用 DbDataAdapter.Update 时插入(如上面的代码)。当我自己构建 cmd 时,我为 FLOAT(126) 列提供 DbParameter.DbType = DbType.Double。如果我自己构建它,所有正常的双精度值都会被接受。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 个提供商?
感谢和最好的问候 - 马蒂
【问题讨论】:
根据我的经验,ODP.NET 有一些奇怪的行为,有时还有奇怪的错误,具体取决于您使用的版本和使用的 Oracle 客户端等。遗憾的是,System.Data.OracleClient
目前已被弃用......这些事情导致我使用商业提供商......很高兴,到目前为止有一个错误(立即纠正)并且从未回头......我不知道这是否适合你......跨度>
显示此行为的表是否有主键?
是的,它是“ID”。我进一步对其进行了测试并像上面一样获取了最大行,但随后将新行的值从第一个 FLOAT(126) 列更改为最后一列(NUMBERs 和 FLOAT(126)s 得到 0,VARCHAR2s 得到简短的无意义字符串,DATEs 得到 DateTime.Now ) 没有问题!当我离开第一个浮点字段时,会引发异常,因此它不允许从数据库中获取的值!!!
我刚刚注意到FLOAT (126)
- 这当然很奇怪,尤其是。在 Oracle 数据库中...你能详细说明一下吗?
尝试改用NUMBER
- 它对于 Oracld DB 来说更“自然”,并且精度略高(38 而不是 37.9)。
【参考方案1】:
首先,我认为您应该将此作为错误报告给 Oracle。即使表非常小,也会发生错误。它与索引或主键无关,即使表没有索引也会发生错误。如果将 ID 的值设置为 0,则插入将运行正常。
我设法创建了一个解决方法,虽然它不是一个好的解决方法,但它可能对你的情况来说已经足够了。
解决方法是为 ODP.Net 使用本机 Oracle 客户端类,因此您必须检查您的应用是否配置为 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]);
【讨论】:
感谢 GTG!我自己也找到了一个解决方法,我编辑了这个问题(建立自己)。如果可行,您的解决方法就足够了!代码已经充满了检查提供者的条件,因为参数和模式等的处理方式不同。我今天会尝试测试它,如果它有效,肯定会接受。再次感谢! 再次感谢!它的工作原理很奇怪,因为数据适配器和构建器是 OracleDataAdapter 和 OracleCommandBuilder 的实例。有什么解释吗?在接受之前我会再测试一下。 我又做了一些测试,发现问题似乎出在 commandbuilder.GetInsertCommand() 中。我的测试是对所有对象使用 _provFactory.Create... 并使用 System.Data 数据类型。这很好用,如果你这样做: dataAdapter.InsertCommand = ((Oracle.DataAccess.Client.OracleCommandBuilder)cmdBuilder).GetInsertCommand();这看起来很奇怪——人们会认为这是一个虚函数,所以这个类型转换应该没有效果。 确实很奇怪。无论如何问题“解决了”。很奇怪,这个常见操作中的错误没有被注意到(根据我在网络上的搜索)。也许没有人真正使用 ODP.NET,因为它充满了错误。【参考方案2】:你有没有尝试在填表后添加一个PK?
_dataSet.Tables[tableName].PrimaryKey = (new List<DataColumn>() _dataSet.Tables[0].Columns["ID"] ).ToArray();
【讨论】:
什么是 CurrentDataSet.Tables[0]?但如果您的意思是尝试将 ID 列添加为 PK 我没有。我试试看。 添加“ID”作为PK没有效果:( 对不起,那是 _dataSet。我已经更新了我的答案,尽管您似乎已经想通了并尝试了。以上是关于为啥我在调用 DbDataAdapter.Update 时使用 ODP.NET OracleDataAdapter 而不是使用 System.Data.OracleClient 的适配器得到 Orac的主要内容,如果未能解决你的问题,请参考以下文章