接獲報案,某 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
{
staticstring csOra = "Data Source=...;User ID=...;Password=....";
staticstring csSql = "Data Source=(local);Integrated Security=SSPI;";
staticvoid 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();
}
}
staticvoid 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();
}
}
staticvoid 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);
}
}
}
staticvoid 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逃避問題。