根本原因是数据库与.net驱动字符集未对齐;需先查nls_characterset(如al32utf8/zhs16gbk),再在连接字符串中设unicode=true或charset=al32utf8,并对clob显式用encoding.utf8解码。

.NET 访问 Oracle 中文乱码,根本不是代码写错了,而是字符“翻译官”没对上号——数据库用一种编码存,.NET 驱动用另一种编码读,中间没协商好,中文自然变成问号、方块或一堆乱码。
查清数据库真实字符集是第一步,别猜
所有后续配置都得对齐这个值,否则全是白忙活。
- 执行
SELECT * FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER = 'NLS_CHARACTERSET';,看返回值是AL32UTF8、ZHS16GBK还是US7ASCII -
US7ASCII是硬伤:它根本不支持中文,此时任何客户端设置都救不了,必须重建库或重导数据 - 生产环境绝大多数是
AL32UTF8,少数老系统是ZHS16GBK;两者不能混用
Oracle.ManagedDataAccess 连接字符串必须加 Unicode=True
旧版 Oracle.DataAccess(ODP.NET Unmanaged)严重依赖系统级 NLS_LANG,而托管驱动 Oracle.ManagedDataAccess 可绕过它,但必须显式声明编码意图。
- 连接字符串里加
Unicode=True:强制驱动以 UTF-16 与 .NETstring对齐,对VARCHAR2/CHAR生效 - 如果数据库是
ZHS16GBK,Unicode=True仍可用,但更稳妥的是补上Charset=ZHS16GBK(注意:仅适用于非 Unicode 模式,二者选一) - 别写
Charset=UTF-8—— Oracle 不接受这个值,正确写法是Charset=AL32UTF8 - 示例:
Data Source=ORCL;User Id=scott;Password=tiger;Unicode=True;
OUT 参数和 REF CURSOR 返回中文时容易踩坑
即使连接正常,存储过程返回的中文仍可能乱码,问题常出在参数绑定和结果集元数据上。
-
OracleParameter必须显式设OracleDbType = OracleDbType.Varchar2,别靠自动推断,也别误用Char或NChar - 处理
REF CURSOR时,确保OracleCommand.BindByName = true,且结果集中字段类型为VARCHAR2或NVARCHAR2 - 如果字段定义为
CHAR(10)存中文,右补空格 + 截断风险高,建议在建表/视图时统一改用VARCHAR2 - 调试时可先用 SQL*Plus 或 PL/SQL Developer 直连执行存储过程,确认原生输出是否正常,排除程序层干扰
CLOB 字段读取必须手动指定 Encoding.UTF8
OracleLob 读出来的是原始字节流,不是字符串;.NET 默认用 Encoding.Default(Windows 上通常是 GBK)去解码 UTF-8 字节,必然乱码。
- 别写
new StreamReader(lob)—— 它默认用Encoding.Default,一读中文就崩 - 必须写
new StreamReader(lob, Encoding.UTF8)(对应数据库字符集为AL32UTF8) - 如果 CLOB 写入时用了
Encoding.Unicode(即 UTF-16 LE),读取时也必须用Encoding.Unicode,否则首字符错位 - 更可控的做法:用
OracleLob.Read(byte[], int, int)分块读字节,再用Encoding.UTF8.GetString(bytes)转字符串 - 注意:
OracleLob.Length返回的是字符数,不是字节数;UTF-8 下一个汉字占 3 字节,别拿它直接当 buffer 长度用
关键点其实就两个:数据库字符集必须可存中文,.NET 驱动必须按同一套规则解码。中间任何一个环节自作主张搞“智能转换”,比如手动 Encode/Decode、依赖环境变量又不重启进程、或让不同工具用不同 NLS_LANG 值连同一个库——乱码就是必然结果。











