不能直接用常规 DataReader 读取 LONG 字段,否则会抛出 System.InvalidCastException 或静默截断;必须设置 InitialLONGFetchSize 和 FetchSize,再通过 GetOracleLong() 获取 OracleLong 实例,调用 Value 或 AsStream() 安全读取。
不能直接用常规 datareader 读取 long 字段,否则会抛出 system.invalidcastexception 或静默截断——这是 c# oracle 迁移中最常踩的坑。
为什么 OracleDataReader 读 LONG 会失败
Oracle 的 LONG 类型在 OCI 层面不支持随机访问,.NET 的 Oracle.ManagedDataAccess(包括旧版 ODP.NET)默认将其映射为 object,但实际底层需要分段拉取。直接调用 reader.GetValue(i) 或 reader.GetString(i) 会触发不完整读取或类型转换异常。
-
LONG字段在查询结果集中表现为“延迟流式对象”,不是普通字符串 - ODP.NET 从 19c 开始仍不支持
LONG的自动流式解包,必须显式使用GetOracleLong()或GetStream() - 如果表里有多个
LONG字段,顺序读取必须严格按列序,跳过或重排会导致后续字段读取失败
OracleCommand 必须启用 FetchSize 和 InitialLONGFetchSize
不设这两个参数,LONG 字段根本不会被完整传输到客户端——哪怕你后面用了正确方法读,拿到的也只是空或前几十字节。
- 在执行
OracleCommand.ExecuteReader()前,必须设置:cmd.InitialLONGFetchSize = -1;(-1 表示全量加载,适用于 ≤ 32KB;若更大,需设具体值并配合流读) - 同时建议设
cmd.FetchSize = 1024 * 1024;(1MB),避免单行LONG被拆成上百次小网络包 - 该设置只对当前
OracleCommand生效,不可复用到其他命令
读取 LONG 字段的唯一可靠方式:用 OracleLong 对象
别试图转成 string 或 byte[] 一次性拿,尤其当字段可能超 32KB 时。正确路径是获取 OracleLong 实例,再用其 Value 或 AsStream()。
- 先确认字段类型:
if (reader.GetFieldType(i) == typeof(Oracle.ManagedDataAccess.Types.OracleLong)) - 再安全取值:
var longObj = reader.GetOracleLong(i); string content = longObj?.Value?.ToString(); - 若内容很大(如存二进制日志、XML 片段),改用流:
using var stream = longObj.AsStream();,然后逐块读取写入目标库 - 注意:
OracleLong.Value是 lazy-evaluated,首次访问才真正拉数据;若中途连接断开,会抛OracleException
写入目标库时,LONG 字段必须用 OracleParameter 显式指定类型
即使源字段是 LONG,目标 INSERT 语句中若用 parameter.Value = string,Oracle 服务端可能误判为 VARCHAR2 并截断(尤其 > 4000 字节)。
- 插入前必须声明参数类型:
var param = cmd.Parameters.Add("p_long", OracleDbType.Long); - 赋值时传
string或byte[]都可以,ODP.NET 会自动适配;但不要传DBNull.Value以外的 null,LONG不支持 NULL 安全写入 - 批量插入时,每个含
LONG的参数都得单独设类型,不能靠“推断”
真正麻烦的不是代码多几行,而是 LONG 的行为在不同 Oracle 版本、不同 ODP.NET 版本、不同字符集下都不一致——比如 ZHS16GBK 下中文内容可能被错误截断,而 AL32UTF8 下又可能因 BOM 头引发解析失败。迁移前务必用真实数据做端到端验证,而不是只测空值或短文本。


















