同事報案,Dapper 查詢 ORACLE 時使用 COALESCE()遇到 ORA-12704: character set mismatch(字元設定不符) 錯誤。
我用以下程式成功重現問題:(jefftest2.t 欄位為 NVARCHAR2)
staticvoid Main(string[] args)
{
using (var cn = new OracleConnection(csStr))
{
var list = cn.Query(
"select 1 from jefftest2 where coalesce(t, :text) like '%'",
new { text = "ABC" });
Console.Write(list.Count());
Console.Read();
}
}
執行結果如下:
有趣的是,將 COALESCE(t, :text) 改成 NVL(t, :text) 就不會出錯。爬文查到應是 COALESCE(NVARCHAR2, VARCHAR2) 前後字串型別不一致,有人想出 N'' || :text 鋸箭小密技。試了一下,還真的有效!
問題來了,為什麼 :text 會變成 VARCHAR2?想起以前遇過類似問題-Dapper+ODP.NET無法寫入Unicode問題,莫非是相同原因?改用 ODP.NET OracleCommand 測試,指定 OracleDbType.NVarchar2,執行正常:
由此推測,八九不離十又是 Dapper 踩中 ODP.NET Bug 的老問題,搬出上回的 Hacking 修補大法:
staticvoid FixOdpNetDbTypeStringMapping()
{
Assembly asm = typeof(OracleConnection).Assembly;
Type tOraDb_DbTypeTable = asm.GetType("Oracle.ManagedDataAccess.Client.OraDb_DbTypeTable");
var fldDbTypeMapping = tOraDb_DbTypeTable.GetField("dbTypeToOracleDbTypeMapping",
BindingFlags.Static | BindingFlags.NonPublic);
int[] mappings = (int[])fldDbTypeMapping.GetValue(null);
mappings[(int)System.Data.DbType.String] = (int)OracleDbType.NVarchar2;
fldDbTypeMapping.SetValue(null, mappings);
}
staticvoid Main(string[] args)
{
FixOdpNetDbTypeStringMapping();
using (var cn = new OracleConnection(csStr))
{
var list = cn.Query(
"select 1 from jefftest2 where coalesce(t, :text) like '%'",
new { text = "ABC" });
Console.Write(list.Count());
Console.Read();
}
}
問題排除~
依上次研究中,原以為要集滿「Oracle資料庫未採AL32UTF8編碼 + OracleParameter參數型別指定DbType + 內容剛好有ANSI/BIG5難字」三項條件才會踩坑,但依這回經驗,參數用於 COALESCE() 不一定要有難字也會出錯,看起來,未來應該將這個 Hacking 修補納入 Dapper + ODP.NET 開發的 SOP 比較保險。