【问题标题】:Error selecting column recently inserted with ALTER TABLE in ODP.NET在 ODP.NET 中选择最近使用 ALTER TABLE 插入的列时出错
【发布时间】:2013-10-10 05:55:37
【问题描述】:

1- 使用 Oracle.DataAccess 连接到 Oracle XE

2-执行命令向表中添加列:alter table TABLE add COLUMN b int;

3- 执行命令选择该列

4- 使用 DataReader 读取。应用程序引发 IndexOutOfRangeException:无法在结果集中找到指定的列

5- 重启应用,查询正常运行

为什么DataReader无法访问我刚才创建的列?

这是一个大而简单的测试代码:

private void button1_Click(object sender, EventArgs e)
{
    using (OracleConnection con = new OracleConnection(Settings.Default.CS))
    {
        con.Open();
        try
        {
            using (OracleCommand com = new OracleCommand())
            {
                com.Connection = con;

                // Create a test table
                com.CommandText = "CREATE TABLE Test (a int)";
                com.ExecuteNonQuery();

                // Add one column
                com.CommandText = "ALTER TABLE Test ADD b int";
                com.ExecuteNonQuery();

                com.CommandText = "SELECT * FROM Test";
                using (DbDataReader dr = com.ExecuteReader())
                {
                    MessageBox.Show(dr.FieldCount.ToString());
                    // Here is showing "2", thats ok
                }
            }
        }
        finally
        {
            con.Close();
        }
    }
}

private void button2_Click(object sender, EventArgs e)
{
    using (OracleConnection con = new OracleConnection(Settings.Default.CS))
    {
        con.Open();
        try
        {
            using (OracleCommand com = new OracleCommand())
            {
                OracleTransaction trans = con.BeginTransaction();
                try
                {
                    // Add a column to table already created
                    com.Connection = con;
                    com.CommandText = "ALTER TABLE Test ADD c int";
                    com.ExecuteNonQuery();

                    // Insert a value, ok
                    com.CommandText = "INSERT INTO TEST (a, b, c) VALUES (1, 2, 3)";
                    com.ExecuteNonQuery();

                    trans.Commit();
                }
                catch
                {
                    trans.Rollback();
                    throw;
                }

                // Selecting only "c" column
                com.CommandText = "SELECT c FROM Test";
                using (DbDataReader dr = com.ExecuteReader())
                {
                    if (dr.Read())
                        MessageBox.Show(Convert.ToInt32(dr["c"]).ToString());
                        // Showing correct value, ok
                }

                // Uncomment these lines to solve problem
                //con.Close();
                //OracleConnection.ClearAllPools();
                //con.Open();

                // Selecting all fields * from table
                com.CommandText = "SELECT * FROM Test";
                using (DbDataReader dr = com.ExecuteReader())
                {
                    MessageBox.Show(dr.GetSchemaTable().Rows.Count.ToString() + " / " + dr.FieldCount.ToString());
                    // HERE IS THE PROBLEM: message are showing 2/2, but table haves 3 fields

                    if (dr.Read())
                        MessageBox.Show(Convert.ToInt32(dr["c"]).ToString());
                    // Here throws IndexOutOfRangeException: Unable to find specified column in result set
                }
            }
        }
        finally
        {
            con.Close();
        }
    }
}

【问题讨论】:

  • 对我来说这听起来像是某种元数据缓存。也许OracleConnection.PurgeStatementCache() 方法可以提供帮助?
  • 我在添加列之后和选择之前尝试了 PurgeStatementCache,但没有成功。
  • OracleTransaction 应该被处理掉,所以应该有它自己的 using 块。您可以选择使用OracleCommand.CreateCommand,这样您就无需自己为命令分配连接。

标签: c# .net oracle odp.net


【解决方案1】:

听起来当您执行阅读器时插入事务尚未提交。我认为您可以通过在插入后关闭连接并在执行阅读器之前再次打开连接来验证这一点(您可能需要使用 OracleConnection.ClearAllPools)

【讨论】:

  • 在我们的实际应用程序中,我们使用事务并且行为完全相同。重新打开连接未解决,但重新启动应用程序已解决。
  • 是否包括调用 OracleConnection.ClearAllPools?如果你们使用连接池,即使您调用“关闭”,框架也会将连接对象保留在池中
  • ClearAllPools 工作!我执行命令添加列,关闭连接,执行 clearAllPools 并重新打开连接。工作正常,我不需要重新启动所有应用程序。谢谢!
  • 我很高兴听到这个消息,连接池是一件美好的事情,但有时它可能会让人头疼:)
【解决方案2】:

尝试使用OracleConnection.BeginTransaction 创建事务,然后在选择之前使用Commit,如文档中example 中所示。

【讨论】:

  • 我尝试在添加列之前启动事务,并在插入值后提交,但也没有成功。
  • 您能否编辑您的示例以显示您对交易的使用?
  • 谢谢。它可能没有任何区别,但是您可以尝试使用BeginTransaction 返回的OracleTransaction 对象并可能更新OracleCommand 中的对象,例如com.Transaction = con.BeginTransaction();?
猜你喜欢
  • 1970-01-01
  • 2019-07-03
  • 2019-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-24
相关资源
最近更新 更多