为什么Oracle ODP.Net在使用参数数组绑定时会自动提交插入的记录?

时间:2016-01-28 20:38:06

标签: arrays oracle binding parameters odp.net

奇怪的问题。我有一个托管的OPD.Net应用程序,它调用存储过程来插入记录。当我正常调用该过程并回滚事务时,记录不会保存到表中(呃!)。当我使用带参数数组绑定的过程并仍然回滚事务时,记录将保存到表中。不知何故,即使我做回滚,记录也会被提交!

TEST Schema:

people loves ocean looks fire and enjoy forest

测试代码:

CREATE TABLE TEST
(
   ID NUMBER(15,0),
   VALUE VARCHAR2(50 CHAR)
)
/

CREATE SEQUENCE TEST_ID_SEQ
  INCREMENT BY 1
  START WITH 50
  MINVALUE 1
  MAXVALUE 999999999999999
  NOCYCLE
  NOORDER
  CACHE 100
/

CREATE OR REPLACE PROCEDURE TEST_INSERT
(
   iVALUE IN VARCHAR2,
   oID OUT NUMBER
)
AS
BEGIN
    oID := TEST_ID_SEQ.NEXTVAL;
    INSERT INTO TEST
    (
      ID,
      VALUE
    )
    VALUES
    (
      oID,
      iVALUE
    );
END;
/

首先运行控制台输出:

using Oracle.ManagedDataAccess.Client;
using Oracle.ManagedDataAccess.Types;
using System;
using System.Data;

namespace OdpTestArrayBinding
{
    class Program
    {
        private const string cConnectioString = "Data Source=DB_DEV;User Id=TMP;Password=sqlsql";
        static void Main(string[] args)
        {
            using (OracleConnection lConnectionA = new OracleConnection(cConnectioString))
            {
                lConnectionA.StateChange += ConnectionStateChanged;
                lConnectionA.Open();
                Console.WriteLine($"[Connection={lConnectionA.GetHashCode()}] Connection opened.");
                int lStartCount = CountTestTableRows(lConnectionA);
                Console.WriteLine($"[Connection={lConnectionA.GetHashCode()}] Number of rows in table at start is {lStartCount}.");
                using (OracleTransaction lTransaction = lConnectionA.BeginTransaction())
                {
                    Console.WriteLine($"[Connection={lConnectionA.GetHashCode()}] Transaction started.");
                    try
                    {
                        using (OracleCommand lCmd = new OracleCommand())
                        {
                            lCmd.Connection = lConnectionA;
                            lCmd.BindByName = true;
                            lCmd.CommandType = System.Data.CommandType.StoredProcedure;
                            lCmd.CommandText = "TEST_INSERT";

                            lCmd.Parameters.Add("iVALUE", OracleDbType.Varchar2, ParameterDirection.Input);
                            // The OracleDbType of the output does not seem to matter, the actual value is always OracleDecimal
                            lCmd.Parameters.Add("oID", OracleDbType.Int64, ParameterDirection.Output);

                            lCmd.ArrayBindCount = 3;

                            lCmd.Parameters["iVALUE"].Value = new string[] { "Foo", "Bar", "Boo" };
                            // Not required.
                            //lCmd.Parameters["oID"].Value = new long[] { -1, -1, -1 }; 

                            lCmd.ExecuteNonQuery();

                            OracleDecimal[] lOutIds = (OracleDecimal[])lCmd.Parameters["oID"].Value;
                            Console.WriteLine($"[Connection={lConnectionA.GetHashCode()}] Inserted 3 rows using stored procedure, out ID vales are {string.Join(",", lOutIds)}.");
                        }
                        ListRows(lConnectionA, lStartCount + 3);

                        using (OracleConnection lConnectionB = new OracleConnection(cConnectioString))
                        {
                            lConnectionB.StateChange += ConnectionStateChanged;
                            lConnectionB.Open();
                            Console.WriteLine($"[Connection={lConnectionB.GetHashCode()}] Connection opened.");
                            ListRows(lConnectionB, lStartCount);
                        }
                    }
                    finally
                    {
                        lTransaction.Rollback();
                        Console.WriteLine($"[Connection={lConnectionA.GetHashCode()}] Transaction rolled back.");
                    }
                }
            }
            Console.WriteLine("Press the ENTER key to continue...");
            Console.ReadLine();
        }

        private static void ConnectionStateChanged(object sender, StateChangeEventArgs e)
        {
            Console.WriteLine($"[Connection={sender.GetHashCode()}] State changed from {e.OriginalState} to {e.CurrentState}.");
        }

        private static int CountTestTableRows(OracleConnection aConnection)
        {
            using (OracleCommand lCmd = new OracleCommand())
            {
                lCmd.Connection = aConnection;
                lCmd.BindByName = true;
                lCmd.CommandType = System.Data.CommandType.Text;
                lCmd.CommandText = "SELECT COUNT(*) FROM TEST";
                return Convert.ToInt32(lCmd.ExecuteScalar());
            }
        }

        private static void ListRows(OracleConnection aConnection, int aExpectedRowCount)
        {
            int lCount = CountTestTableRows(aConnection);
            Console.Write($"[Connection={aConnection.GetHashCode()}] Number of rows in table {lCount}");
            if (lCount == aExpectedRowCount)
                Console.WriteLine(" (Test passed, actual and expected row count are the same).");
            else
                Console.WriteLine($" (Test FAILED!, expected {aExpectedRowCount} rows).");
        }
    }
}

我尝试过Oracle.ManagedDataAccess.dll 4.121.2.20141216(ODAC RELEASE 3)和4.121.2.20150926(ODAC RELEASE 4),两者都给出了相同的结果。任何想法或解决方法?

3 个答案:

答案 0 :(得分:0)

请检查您的命令:您忘记在命令上分配事务了吗?

    lCmd.Connection = lConnectionA;
  // Assign transaction to your command
   lCmd.Transaction = lTransaction;

答案 1 :(得分:0)

您是否可以尝试更改代码以使用此示例。 只打开一个使用块进行连接。

public void RunOracleTransaction(string connectionString)
{
    using (OracleConnection connection = new OracleConnection(connectionString))
    {
        connection.Open();

        OracleCommand command = connection.CreateCommand();
        OracleTransaction transaction;

        // Start a local transaction
        transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
        // Assign transaction object for a pending local transaction
        command.Transaction = transaction;

        try
        {
            command.CommandText = 
                "INSERT INTO Dept (DeptNo, Dname, Loc) values (50, 'TECHNOLOGY', 'DENVER')";
            command.ExecuteNonQuery();
            command.CommandText = 
                "INSERT INTO Dept (DeptNo, Dname, Loc) values (60, 'ENGINEERING', 'KANSAS CITY')";
            command.ExecuteNonQuery();
            transaction.Commit();
            Console.WriteLine("Both records are written to database.");
        }
        catch (Exception e)
        {
            transaction.Rollback();
            Console.WriteLine(e.ToString());
            Console.WriteLine("Neither record was written to database.");
        }
    }
}

答案 2 :(得分:0)

您应该创建一个TransactionScope来包装数据库连接,并在您的TransactionScope内,尝试加入环境事务:

<your connection object>.EnlistTransaction(Transaction.Current);