在 .NET 中读写 Oracle BLOB 字段时,读取阶段必须保持 Connection 始终处于打开状态,因为 Oracle.ManagedDataAccess 返回的并不是 byte[],而是延迟加载的 OracleBlob 对象;如果连接提前关闭,后续访问就会抛出 InvalidOperationException。写入 Oracle BLOB 之前,则需要先使用 EMPTY_BLOB() 进行占位,再通过 SELECT FOR UPDATE 获取可更新引用,并且整个读写流程必须放在同一个事务中完成。

Oracle BLOB读取时Connection必须保持打开状态
很多开发者认为直接调用 OracleCommand.ExecuteScalar() 并返回 byte[] 是最简便的方案,但在 Oracle .NET 驱动(Oracle.ManagedDataAccess)中,BLOB 字段默认返回的通常是延迟加载的 OracleBlob 对象,而不是真正的字节数组。只要 OracleConnection 被关闭,再去访问该对象的 Value,或者调用 CopyTo() 之类的方法,就会触发 InvalidOperationException: "Connection is closed." 错误。
实操建议:
- 读取 Oracle BLOB 之前,先确认
OracleConnection.State == ConnectionState.Open - 使用
OracleBlob.GetStream()获取数据流后,应立即完成读取操作(如stream.ReadBytes()),不要跨越连接生命周期去缓存流对象或OracleBlob实例 - 如果需要异步读取,可使用
await blobStream.CopyToAsync(destStream),但在整个流读取过程中,数据库连接仍必须保持打开 - 不要把
OracleBlob保存到静态变量或长生命周期对象中,以免后续访问失效
写入BLOB前必须先插入空LOB占位符
在 Oracle 数据库中,INSERT 语句并不适合直接写入大块二进制内容,尤其当数据超过 32KB 时,通常很容易遇到 ORA-01461: can bind a LONG value only for insert into a LONG column 错误。更稳定、也更常见的 Oracle BLOB 写入方式通常分为两步:先执行 INSERT,让 BLOB 字段通过 EMPTY_BLOB() 预留位置;随后再使用 SELECT FOR UPDATE 获取可更新的 OracleBlob 引用,最后把实际的二进制数据写入该字段。
实操建议:
- INSERT SQL 可写为
INSERT INTO table (id, name, content) VALUES (:id, :name, EMPTY_BLOB()) - 随后立即执行
SELECT content FROM table WHERE id = :id FOR UPDATE,并确保CommandBeha vior.SequentialAccess不要启用(否则会影响更新能力) - 从
OracleDataReader取到OracleBlob后,可调用blob.Write(byteArray, 0, byteArray.Length)或blob.SetStream(stream)完成写入 - 整个 Oracle BLOB 写入流程必须放在同一个
OracleTransaction中,否则FOR UPDATE锁定将无法生效
大文件写入需控制缓冲区与超时设置
当写入几十 MB 甚至更大的 BLOB 文件时,默认的 CommandTimeout(30 秒)以及网络缓冲区设置,往往容易引发超时,或者出现 OracleException: ORA-24345: A Truncation occurred。需要注意的是,这个报错很多时候并不代表真实的数据截断,更可能是底层 TCP 数据包重组失败带来的误导性异常。
实操建议:
- 建议显式设置
OracleCommand.CommandTimeout = 300(单位为秒),并根据上传文件大小适当线性增加超时时间 - 不要一次性把整个大文件加载到内存中,推荐使用
FileStream+OracleBlob.SetStream()进行流式写入,以优化内存占用和稳定性 - 在连接字符串中增加
EnableExtendedMetaData=False,可在一定程度上提升大 BLOB 的读写性能,减少元数据反射开销 - 如果项目部署在 IIS 环境下,还需要同步检查
web.config中的maxRequestLength和executionTimeout,因为它们可能会先于数据库层中断文件上传
.NET Core/.NET 5+ 中 Oracle.ManagedDataAccess 版本兼容陷阱
Oracle 最新版本驱动在 .NET Core/.NET 平台上的兼容性存在明显的版本门槛:21.x 及以上版本仅支持 .NET 5+;而 19c 驱动(19.11)虽然官方说明可兼容 .NET Core 3.1,但在 Linux 环境下进行 Oracle BLOB 读写时,依然可能偶发 System.AccessViolationException,尤其是在直接调用 OracleBlob.Value 的场景中更要格外注意。
实操建议:
- 生产环境建议优先使用
Oracle.ManagedDataAccess 21.11(或当前最新稳定版),并确认目标运行时为 .NET 5 及以上版本 - 如果必须继续使用 .NET Core 3.1,建议降级到
Oracle.ManagedDataAccess 19.10,同时避免调用OracleBlob.Value,统一改为GetStream()+ReadAsync()方式读取 - 在 Linux 部署环境中,务必确认
libaio1已正确安装(apt-get install libaio1),否则可能导致 Oracle BLOB 操作静默失败
OracleCommand 混合使用时,Dapper 默认不会直接暴露 OracleBlob,通常还需要通过一层 IDataReader 才能获取可更新的 BLOB 引用。