同事報案,Dapper 查詢 ORACLE 時使用 COALESCE() 遇到 ORA-12704: character set mismatch(字元設定不符) 錯誤。

我用以下程式成功重現問題:(jefftest2.t 欄位為 NVARCHAR2)

        static void 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 修補大法

static void 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);
}
 
static void 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 比較保險。


Comments

Be the first to post a comment

Post a comment