接獲報案,某 Oracle Package 使用 raise_application_error 抛回自訂錯誤代碼與錯誤訊息(其中包含輸入參數以利偵錯),使用 ODP.NET 呼叫時理應可在 Exception.Message 看到自訂錯誤訊息,但某支程式出錯時卻只傳回錯誤代碼並抱怨找不到該代碼對應訊息:ORA-20001: Message 20001 not found;  product=RDBMS; facility=ORA

經過調查與對照測試,發現與程式被包在 TransactionScope 有關。用以下程式重現與驗證問題:

using Oracle.DataAccess.Client;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Transactions;
 
namespace OraExpLab
{
    class Program
    {
        static string csOra = "Data Source=...;User ID=...;Password=....";
        static string csSql = "Data Source=(local);Integrated Security=SSPI;";
 
        static void querySqlServer()
        {
            using (var cn = new SqlConnection(
                       csSql + "Application Name=" + Guid.NewGuid().ToString()))
            {
                var cmd = new SqlCommand("SELECT getdate() as D", cn);
                cn.Open();
                var dr = cmd.ExecuteReader();
                dr.Read();
                Console.WriteLine(dr["D"]);
                cn.Close();
            }
        }
 
        static void queryOraServer()
        {
            using (OracleConnection cn = new OracleConnection(csOra))
            {
                cn.Open();
                var cmd = cn.CreateCommand();
                cmd.CommandText = "SELECT SYSDATE as D FROM DUAL";
                var dr = cmd.ExecuteReader();
                dr.Read();
                Console.WriteLine(dr["D"]);
                cn.Close();
            }
        }
 
        static void raiseOraError()
        {
            using (OracleConnection cn = new OracleConnection(csOra))
            {
                cn.Open();
                var cmd = cn.CreateCommand();
                cmd.CommandText = @"
declare
begin
    raise_application_error(-20001, '我錯了');
end;
";
                try
                {
                    cmd.ExecuteNonQuery();
                }
                catch (Exception ex)
                {
                    Console.WriteLine("ORACLE CUST ERROR:" + ex.Message);
                }
            }
        }
 
        static void Main(string[] args)
        {
            raiseOraError();
 
            using (var tx = new TransactionScope())
            {
                querySqlServer();
                queryOraServer();
                raiseOraError();
                Console.WriteLine(
                    Transaction.Current.TransactionInformation.LocalIdentifier);
                Console.WriteLine(
                    Transaction.Current.TransactionInformation.DistributedIdentifier);
            }
            Console.Read();
        }
    }
}

用一小段 PL/SQL Script 故意抛回自訂錯誤,單獨呼叫時可由 ex.Message 看到自訂訊息「我錯了」。隨後用 TransactionScope 將其與 SQL 查詢包起來,刻意觸發分散式交易,並由 Transaction.Current.TransactionInformation.DistributedIdentifier 驗證分散式交易已啟動(參考),第二次呼叫傳回錯誤訊息變成 ORA-20001: Message 20001 not found;  product=RDBMS; facility=ORA。

執行結果如下:

ORACLE CUST ERROR:ORA-20001: 我錯了
ORA-06512: 在 line 4
2017/3/28 上午 05:40:59
2017/3/28 上午 05:41:00
ORACLE CUST ERROR:ORA-20001: Message 20001 not found;  product=RDBMS; facility=ORA
d9f57da1-9ad4-4623-b91c-7ac4044fc7c1:1
e01b29b2-888a-4e8c-b612-452a22e357fc

進一步對照測試,發現這現象只發生在使用 Unmanaged ODP.NET,使用 Managed ODP.NET 不管有無分散式交易都可看到自訂錯誤訊息。猜想這與 Oracle Client 的 Unmanaged 程式庫行為有關,現階段遇此困擾,可考慮改用 Managed ODP.NET 逃避問題。


Comments

Be the first to post a comment

Post a comment