OracleBulkCopy构造函数必须传入已打开的OracleConnection,不接受连接字符串或未打开连接,否则抛InvalidOperationException;需先Open()再传入,且连接不能在WriteToServer期间关闭或复用。
很多人卡在第一步:直接 new oraclebulkcopy 传入连接字符串或未打开的连接,结果抛出 invalidoperationexception: connection property has not been initialized or is null。它不接受连接字符串,也不自动打开连接。
正确做法是先显式打开 OracleConnection,再传入构造函数:
using var conn = new OracleConnection(connectionString);conn.Open(); // 必须!using var bulk = new OracleBulkCopy(conn); // ✅
Open 状态,且不能在 bulk.WriteToServer() 执行期间被关闭或复用Pooling=false,否则批量写入性能会断崖下跌OracleBulkCopy 实例跨线程复用——它不是线程安全的OracleBulkCopy 默认按列序号匹配(source[0] → target[0]),但一旦源数据来自 DataTable 且列名和目标表字段名不同,或顺序错位,就会静默写错字段甚至报 ORA-01722: invalid number。
务必用 ColumnMappings.Add() 显式绑定:
bulk.DestinationTableName = "T_LOG";bulk.ColumnMappings.Add("LogId", "LOG_ID");bulk.ColumnMappings.Add("Msg", "MSG_CONTENT");bulk.ColumnMappings.Add("CreateTime", "CREATE_TIME");
DataTable.Columns[i].ColumnName),右侧是目标表字段名(大小写敏感,取决于建表时是否加双引号)IDataReader,列名取自 GetSchemaTable() 中的 ColumnName 字段,不是别名WriteToServer() 时立即抛异常,不会静默跳过默认 BatchSize = 0 表示单事务提交全部行,百万级数据极易触发 Oracle 内存耗尽(ORA-04030: out of process memory)或 .NET 的 OutOfMemoryException。
必须手动设置合理值:
bulk.BatchSize = 8000;bulk.WriteToServer(dataTable);
BatchSize 不影响单次调用总行数上限,只控制内部分批提交粒度ALTER INDEX ... UNUSABLE),导入完成后再重建OracleBulkCopy 不做运行时类型转换。常见崩点:
DateTime 写入 DATE 字段正常,但写入 TIMESTAMP(6) 可能丢纳秒精度,写入 CHAR(10) 直接报 ORA-00932
int 写入 NUMBER(5) 没问题,但写入 NUMBER(3) 超限时抛 ORA-01438,且错误位置不提示具体哪一行NULL 值写入 NOT NULL 字段直接失败,不会用默认值替代上线前务必用小批量数据(含边界值、NULL、超长字符串)预验字段兼容性。生产环境建议配合 OracleBulkCopy.WriteToServerAsync() + try/catch 捕获 OracleException 并记录失败批次起始行号。
真正难的不是调通,而是让百万行里每一列、每一行都刚好落在 Oracle 类型系统的“安全区”内——这需要提前对源数据做清洗,而不是依赖 BulkCopy 自动兜底。