ORA-1461 与托管 ODP.Net
ORA-1461 with Managed ODP.Net
我们最近将我们的解决方案升级到新的托管 ODP.Net(v4.121.2.0,从 v4.121.1.0 开始)并且当字段在 1001 之间时更新到 CLOB 字段时遇到了问题和 4000 个字符。当您尝试这样做时,会从 ODP.Net 中抛出错误 ORA-1461。
运行 相同的代码和数据,使用早期版本的 ODP.Net 并且工作正常。此外,您可以插入包含 1001 和 4000 个字符的记录,只是无法更新它们。
我用 C# 创建了一个示例程序来演示该问题。该程序执行以下操作:
- 它在数据库
中创建了一个 3 列 table,其中一个列是 CLOB
- 它创建一个内存中的 .Net DataSet 对象来匹配。
- 在 CLOB 字段中使用 1400 个字符在 DataSet 中创建一条新记录。
- 使用 INSERT 语句将 DataSet 保存到数据库。
- 用一些新数据更新 DataSet 中的 CLOB 字段,同样是 1400 个字符的数据。
- 将 DataSet 保存到数据库,并抛出 ORA-1461。
我没有 Oracle 支持帐户,那么您在哪里向 Oracle 报告 ODP.Net 问题?
演示问题的示例 C# 代码:
using System;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using Oracle.ManagedDataAccess.Client;
class CLOBTest
{
string _TableName = "CLOBTEST";
string _ServerName = "servername";
string _UserName = "username";
string _Password = "password";
public void CLOBTest1()
{
// Create a physical data table, if needed, in the Oracle DB that has the CLOB column
CreateTable();
// Create a dataset for the CLOBTEST table, fill it with a new row
DataSet CLOBInfo = BuildCLOBTestDataSet();
DataTable CLOBTable = CLOBInfo.Tables[_TableName];
DataRow CLOBRow = CLOBTable.NewRow();
CLOBRow["ACTION_CODE"] = DateTime.Now.ToString("s");
CLOBRow["DESCRIPTION"] = "CLOB Slim Test";
// The size of text in the CLOB field is critical to reproducing this defect.
// It *only* happens when the field has between 1001 and 4000 characters.
int LOBFieldSize = 1400;
string CLOBText = DateTime.Now.ToString("s") + " " + new string('-', LOBFieldSize);
CLOBRow["SCRIPT_TEXT"] = CLOBText.Substring(0, LOBFieldSize);
CLOBTable.Rows.Add(CLOBRow);
// Add that row to the DB, and then mark the DS with AcceptChanges
InsertRow(CLOBInfo);
// Update that row with some new data.
CLOBText = DateTime.Now.ToString("s") + " :: " + CLOBText;
CLOBRow["SCRIPT_TEXT"] = CLOBText.Substring(0, LOBFieldSize);
// Error (ORA-1461) happens in the UPDATE when the CLOB has 1001 - 4000 characters in it.
UpdateRow(CLOBInfo);
}
private void CreateTable()
{
if (TableExists())
return;
using (OracleConnection oc = OpenConnection())
{
using (OracleCommand ocmd = oc.CreateCommand())
{
string SQL = "CREATE TABLE " + _TableName + " (ACTION_CODE VARCHAR2(30) NOT NULL, DESCRIPTION VARCHAR2(50) NOT NULL, SCRIPT_TEXT CLOB, CONSTRAINT CLOBTEST_PK PRIMARY KEY (ACTION_CODE))";
ocmd.CommandText = SQL;
ocmd.ExecuteNonQuery();
AddLogMessage("Table created.");
}
}
}
private bool TableExists()
{
using (OracleConnection oc = OpenConnection())
{
using (OracleCommand ocmd = oc.CreateCommand())
{
string SQL = "SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME = '" + _TableName + "'";
ocmd.CommandText = SQL;
object teRaw = ocmd.ExecuteScalar();
bool te = (bool)(int.Parse(teRaw.ToString()) > 0);
AddLogMessage("Table exists? " + te.ToString());
return te;
}
}
}
private void InsertRow(DataSet CLOBInfo)
{
string SQL = "INSERT INTO " + _TableName + " (ACTION_CODE, DESCRIPTION, SCRIPT_TEXT) VALUES (:pACTION_CODE, :pDESCRIPTION, :pSCRIPT_TEXT)";
using (OracleConnection oc = OpenConnection())
{
using (OracleDataAdapter oda = new OracleDataAdapter())
{
using (OracleCommand ocmd = oc.CreateCommand())
{
CreateDataParameters(ocmd);
ocmd.CommandText = SQL;
oda.InsertCommand = ocmd;
DataRow[] updRows = CLOBInfo.Tables[_TableName].Select(null, null, DataViewRowState.Added);
if (updRows.Length > 0)
{
int rc = oda.Update(updRows);
CLOBInfo.AcceptChanges();
AddLogMessage("Row inserted into CLOBTEST. rc = " + rc.ToString());
}
else
AddLogMessage("No rows to insert.");
}
}
}
}
private void UpdateRow(DataSet CLOBInfo)
{
string SQL = "UPDATE " + _TableName + " SET ACTION_CODE = :pACTION_CODE, DESCRIPTION = :pDESCRIPTION, SCRIPT_TEXT = :pSCRIPT_TEXT WHERE ACTION_CODE = :pOLDACTION_CODE";
using (OracleConnection oc = OpenConnection())
{
using (OracleDataAdapter oda = new OracleDataAdapter())
{
using (OracleCommand ocmd = oc.CreateCommand())
{
ocmd.CommandText = SQL;
CreateDataParameters(ocmd);
OracleParameter kp = new OracleParameter();
kp.ParameterName = "pOLDACTION_CODE";
kp.SourceColumn = "ACTION_CODE";
kp.SourceVersion = DataRowVersion.Original;
ocmd.Parameters.Add(kp);
oda.UpdateCommand = ocmd;
DataRow[] updRows = CLOBInfo.Tables[_TableName].Select(null, null, DataViewRowState.ModifiedCurrent);
if (updRows.Length > 0)
{
int rc = oda.Update(updRows);
CLOBInfo.AcceptChanges();
AddLogMessage("CLOBTEST row updated. rc = " + rc.ToString());
}
else
AddLogMessage("No rows to update.");
}
}
}
}
private void CreateDataParameters(OracleCommand ocmd)
{
OracleParameter pActionCode = new OracleParameter();
pActionCode.ParameterName = "pACTION_CODE";
pActionCode.SourceColumn = "ACTION_CODE";
ocmd.Parameters.Add(pActionCode);
OracleParameter pDescription = new OracleParameter();
pDescription.ParameterName = "pDESCRIPTION";
pDescription.SourceColumn = "DESCRIPTION";
ocmd.Parameters.Add(pDescription);
OracleParameter pScriptText = new OracleParameter();
pScriptText.ParameterName = "pSCRIPT_TEXT";
pScriptText.SourceColumn = "SCRIPT_TEXT";
ocmd.Parameters.Add(pScriptText);
}
private DataSet BuildCLOBTestDataSet()
{
DataSet ads = new DataSet();
DataTable at = new DataTable("CLOBTEST");
DataColumn ac = at.Columns.Add("ACTION_CODE", typeof(string));
at.Columns.Add("DESCRIPTION", typeof(string));
at.Columns.Add("SCRIPT_TEXT", typeof(string));
at.PrimaryKey = new DataColumn[] { ac };
ads.Tables.Add(at);
return ads;
}
private OracleConnection OpenConnection()
{
OracleConnection oc = null;
try
{
OracleClientFactory ocf = new OracleClientFactory();
DbConnectionStringBuilder csb = ocf.CreateConnectionStringBuilder();
csb["Data Source"] = _ServerName;
csb["User ID"] = _UserName;
csb["Password"] = _Password;
string cs = csb.ConnectionString;
oc = new OracleConnection(cs);
oc.Open();
AddLogMessage("Connection opened.");
}
catch (Exception ex)
{
AddLogMessage("Error Opening Connection! " + ex.Message);
throw;
}
return oc;
}
private void AddLogMessage(string msg)
{
Debug.WriteLine(string.Format("{0:T} - {1}", DateTime.Now, msg));
}
}
此线程中显示了两个解决方法:https://community.oracle.com/thread/3649551
解决方法一:
将您的 CLOB 参数配置为 OracleDbType.Clob
和 ParameterDirection.InputOutput
.
解决方法二:
将您的 CLOB 参数配置为 OracleDbType.Clob
并将值明确设置为 OracleClob
对象。
修复:
Oracle 现在已知该错误,因此希望尽快发布修复版本。
编辑: Patch 20361140 应该修复它。
我们最近将我们的解决方案升级到新的托管 ODP.Net(v4.121.2.0,从 v4.121.1.0 开始)并且当字段在 1001 之间时更新到 CLOB 字段时遇到了问题和 4000 个字符。当您尝试这样做时,会从 ODP.Net 中抛出错误 ORA-1461。
运行 相同的代码和数据,使用早期版本的 ODP.Net 并且工作正常。此外,您可以插入包含 1001 和 4000 个字符的记录,只是无法更新它们。
我用 C# 创建了一个示例程序来演示该问题。该程序执行以下操作:
- 它在数据库 中创建了一个 3 列 table,其中一个列是 CLOB
- 它创建一个内存中的 .Net DataSet 对象来匹配。
- 在 CLOB 字段中使用 1400 个字符在 DataSet 中创建一条新记录。
- 使用 INSERT 语句将 DataSet 保存到数据库。
- 用一些新数据更新 DataSet 中的 CLOB 字段,同样是 1400 个字符的数据。
- 将 DataSet 保存到数据库,并抛出 ORA-1461。
我没有 Oracle 支持帐户,那么您在哪里向 Oracle 报告 ODP.Net 问题?
演示问题的示例 C# 代码:
using System;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using Oracle.ManagedDataAccess.Client;
class CLOBTest
{
string _TableName = "CLOBTEST";
string _ServerName = "servername";
string _UserName = "username";
string _Password = "password";
public void CLOBTest1()
{
// Create a physical data table, if needed, in the Oracle DB that has the CLOB column
CreateTable();
// Create a dataset for the CLOBTEST table, fill it with a new row
DataSet CLOBInfo = BuildCLOBTestDataSet();
DataTable CLOBTable = CLOBInfo.Tables[_TableName];
DataRow CLOBRow = CLOBTable.NewRow();
CLOBRow["ACTION_CODE"] = DateTime.Now.ToString("s");
CLOBRow["DESCRIPTION"] = "CLOB Slim Test";
// The size of text in the CLOB field is critical to reproducing this defect.
// It *only* happens when the field has between 1001 and 4000 characters.
int LOBFieldSize = 1400;
string CLOBText = DateTime.Now.ToString("s") + " " + new string('-', LOBFieldSize);
CLOBRow["SCRIPT_TEXT"] = CLOBText.Substring(0, LOBFieldSize);
CLOBTable.Rows.Add(CLOBRow);
// Add that row to the DB, and then mark the DS with AcceptChanges
InsertRow(CLOBInfo);
// Update that row with some new data.
CLOBText = DateTime.Now.ToString("s") + " :: " + CLOBText;
CLOBRow["SCRIPT_TEXT"] = CLOBText.Substring(0, LOBFieldSize);
// Error (ORA-1461) happens in the UPDATE when the CLOB has 1001 - 4000 characters in it.
UpdateRow(CLOBInfo);
}
private void CreateTable()
{
if (TableExists())
return;
using (OracleConnection oc = OpenConnection())
{
using (OracleCommand ocmd = oc.CreateCommand())
{
string SQL = "CREATE TABLE " + _TableName + " (ACTION_CODE VARCHAR2(30) NOT NULL, DESCRIPTION VARCHAR2(50) NOT NULL, SCRIPT_TEXT CLOB, CONSTRAINT CLOBTEST_PK PRIMARY KEY (ACTION_CODE))";
ocmd.CommandText = SQL;
ocmd.ExecuteNonQuery();
AddLogMessage("Table created.");
}
}
}
private bool TableExists()
{
using (OracleConnection oc = OpenConnection())
{
using (OracleCommand ocmd = oc.CreateCommand())
{
string SQL = "SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME = '" + _TableName + "'";
ocmd.CommandText = SQL;
object teRaw = ocmd.ExecuteScalar();
bool te = (bool)(int.Parse(teRaw.ToString()) > 0);
AddLogMessage("Table exists? " + te.ToString());
return te;
}
}
}
private void InsertRow(DataSet CLOBInfo)
{
string SQL = "INSERT INTO " + _TableName + " (ACTION_CODE, DESCRIPTION, SCRIPT_TEXT) VALUES (:pACTION_CODE, :pDESCRIPTION, :pSCRIPT_TEXT)";
using (OracleConnection oc = OpenConnection())
{
using (OracleDataAdapter oda = new OracleDataAdapter())
{
using (OracleCommand ocmd = oc.CreateCommand())
{
CreateDataParameters(ocmd);
ocmd.CommandText = SQL;
oda.InsertCommand = ocmd;
DataRow[] updRows = CLOBInfo.Tables[_TableName].Select(null, null, DataViewRowState.Added);
if (updRows.Length > 0)
{
int rc = oda.Update(updRows);
CLOBInfo.AcceptChanges();
AddLogMessage("Row inserted into CLOBTEST. rc = " + rc.ToString());
}
else
AddLogMessage("No rows to insert.");
}
}
}
}
private void UpdateRow(DataSet CLOBInfo)
{
string SQL = "UPDATE " + _TableName + " SET ACTION_CODE = :pACTION_CODE, DESCRIPTION = :pDESCRIPTION, SCRIPT_TEXT = :pSCRIPT_TEXT WHERE ACTION_CODE = :pOLDACTION_CODE";
using (OracleConnection oc = OpenConnection())
{
using (OracleDataAdapter oda = new OracleDataAdapter())
{
using (OracleCommand ocmd = oc.CreateCommand())
{
ocmd.CommandText = SQL;
CreateDataParameters(ocmd);
OracleParameter kp = new OracleParameter();
kp.ParameterName = "pOLDACTION_CODE";
kp.SourceColumn = "ACTION_CODE";
kp.SourceVersion = DataRowVersion.Original;
ocmd.Parameters.Add(kp);
oda.UpdateCommand = ocmd;
DataRow[] updRows = CLOBInfo.Tables[_TableName].Select(null, null, DataViewRowState.ModifiedCurrent);
if (updRows.Length > 0)
{
int rc = oda.Update(updRows);
CLOBInfo.AcceptChanges();
AddLogMessage("CLOBTEST row updated. rc = " + rc.ToString());
}
else
AddLogMessage("No rows to update.");
}
}
}
}
private void CreateDataParameters(OracleCommand ocmd)
{
OracleParameter pActionCode = new OracleParameter();
pActionCode.ParameterName = "pACTION_CODE";
pActionCode.SourceColumn = "ACTION_CODE";
ocmd.Parameters.Add(pActionCode);
OracleParameter pDescription = new OracleParameter();
pDescription.ParameterName = "pDESCRIPTION";
pDescription.SourceColumn = "DESCRIPTION";
ocmd.Parameters.Add(pDescription);
OracleParameter pScriptText = new OracleParameter();
pScriptText.ParameterName = "pSCRIPT_TEXT";
pScriptText.SourceColumn = "SCRIPT_TEXT";
ocmd.Parameters.Add(pScriptText);
}
private DataSet BuildCLOBTestDataSet()
{
DataSet ads = new DataSet();
DataTable at = new DataTable("CLOBTEST");
DataColumn ac = at.Columns.Add("ACTION_CODE", typeof(string));
at.Columns.Add("DESCRIPTION", typeof(string));
at.Columns.Add("SCRIPT_TEXT", typeof(string));
at.PrimaryKey = new DataColumn[] { ac };
ads.Tables.Add(at);
return ads;
}
private OracleConnection OpenConnection()
{
OracleConnection oc = null;
try
{
OracleClientFactory ocf = new OracleClientFactory();
DbConnectionStringBuilder csb = ocf.CreateConnectionStringBuilder();
csb["Data Source"] = _ServerName;
csb["User ID"] = _UserName;
csb["Password"] = _Password;
string cs = csb.ConnectionString;
oc = new OracleConnection(cs);
oc.Open();
AddLogMessage("Connection opened.");
}
catch (Exception ex)
{
AddLogMessage("Error Opening Connection! " + ex.Message);
throw;
}
return oc;
}
private void AddLogMessage(string msg)
{
Debug.WriteLine(string.Format("{0:T} - {1}", DateTime.Now, msg));
}
}
此线程中显示了两个解决方法:https://community.oracle.com/thread/3649551
解决方法一:
将您的 CLOB 参数配置为 OracleDbType.Clob
和 ParameterDirection.InputOutput
.
解决方法二:
将您的 CLOB 参数配置为 OracleDbType.Clob
并将值明确设置为 OracleClob
对象。
修复:
Oracle 现在已知该错误,因此希望尽快发布修复版本。
编辑: Patch 20361140 应该修复它。