当DbDataAdapter.Update调用时,为什么我使用ODP.NET OracleDataAdapter获取OracleTruncateException而不使用System.Data.OracleClient的适配器?

cha*_*r m 5 .net c# 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); 
}
Run Code Online (Sandbox Code Playgroud)

这适用于OleDb和System.Data.OracleClient.但是,使用Oracle.DataAccess.Client的提供程序,我得到:

Oracle.DataAccess.Types.OracleTruncateException (16550) 
Run Code Online (Sandbox Code Playgroud)

文本截断结果源于:

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
Run Code Online (Sandbox Code Playgroud)

我得到的表是大表,其他包含61个字段.所有字段的类型仅限于:

VARCHAR2(different lenghts)
VARCHAR2(different lenghts) NOT NULL
FLOAT(126) NOT NULL     
NUMBER NOT NULL
DATE
Run Code Online (Sandbox Code Playgroud)

编辑以防止过多评论:

- 我无法更改数据库中的数据类型或任何内容.

-In 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.即使在调用DbDataAdapter.Update时给任何引发相同的异常值1或2.

要么

  • 我自己创建了DbDataAdapter.Insertommand,并且在调用DbDataAdapter.Update时只有插入(如上面的代码).当我自己构建cmd时,我为FLOAT(126)-columns提供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>
Run Code Online (Sandbox Code Playgroud)

任何想法是什么原因以及我将如何让它适用于所有3个提供商?

谢谢和最诚挚的问候 - 马蒂

GTG*_*GTG 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]);
    }
Run Code Online (Sandbox Code Playgroud)