使用 Dapper 和 ODAC Managed Driver 無法寫入 Unicode 的問題
TLDR
- 問題情境:使用 Dapper 搭配 Oracle.ManagedDataAccess.Core 寫入 Unicode 字元(如簡體中文)時,資料庫端出現亂碼。
- 根本原因:ODP.NET 的驅動程式將
DbType.String預設對應至OracleDbType.Varchar2,而非支援 Unicode 的OracleDbType.NVarchar2。 - 解決方案:無法直接透過 Dapper 的
DynamicParameters設定OracleDbType,需實作自訂的IDynamicParameters類別,手動將OracleParameter加入IDbCommand。
問題分析與成因
當開發者使用 Dapper 進行資料庫操作時,若未特別指定參數型別,Dapper 會依據 DbType 進行對應。在 Oracle 的 Managed Driver 中,DbType.String 被錯誤地映射為 OracleDbType.Varchar2,導致 Unicode 字元在傳輸過程中因編碼不匹配而產生亂碼。即使在程式碼中顯式指定 DbType.String 也無法解決此問題,因為驅動程式內部的對應邏輯並未將其導向 OracleDbType.NVarchar2。
自訂 DynamicParameters 解決方案
由於 Dapper 原生的 DynamicParameters 不支援直接傳入 OracleParameter 物件以指定 OracleDbType,因此需要透過實作 SqlMapper.IDynamicParameters 介面來擴充功能,允許手動加入 IDbDataParameter。
實作步驟
建立一個自訂的 MyDynamicParameters 類別,將參數轉發至 IDbCommand:
csharp
public class MyDynamicParameters : SqlMapper.IDynamicParameters {
private readonly Dapper.DynamicParameters dynamicParameters = new();
private readonly List<IDbDataParameter> dbDataParameters = new();
public void Add(string name, object value, DbType? dbType, ParameterDirection? direction, int? size) {
dynamicParameters.Add(name, value, dbType, direction, size);
}
public void Add(IDbDataParameter paramerter) {
dbDataParameters.Add(paramerter);
}
void SqlMapper.IDynamicParameters.AddParameters(IDbCommand command, SqlMapper.Identity identity) {
((SqlMapper.IDynamicParameters)dynamicParameters).AddParameters(command, identity);
foreach (IDbDataParameter p in dbDataParameters) {
command.Parameters.Add(p);
}
}
}使用方式
在執行 SQL 指令時,改用 MyDynamicParameters 並明確指定 OracleDbType.NVarchar2:
csharp
using (IDbConnection conn = new OracleConnection(connStr)) {
conn.Open();
MyDynamicParameters parameters = new();
parameters.Add(new OracleParameter {
ParameterName = "Name",
Value = value,
OracleDbType = OracleDbType.NVarchar2
});
conn.Query(sql, parameters);
}透過此方式,即可確保參數以正確的 Unicode 格式寫入 Oracle 資料庫,避免亂碼問題。
異動歷程
- 初版文件建立。