为啥我在调用 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的主要内容,如果未能解决你的问题,请参考以下文章
为啥我在调用 ResultSet.refreshRow() 时会看到 NotUpdatable?
为啥我在 iOS 上从 Swift 调用服务时收到 SSL 错误?
为啥当我在 cfoutput 中调用函数时,ColdFusion 会添加空格?
为啥我在 ajax post 调用后得到 json 响应页面?
为啥我在 Django 中使用此调用从 Paypal 收到“内部错误”?
为啥我在尝试调用过程时得到 ORA-06531: reference to uninitialized collection?