oracle不支持sqlbulkcopy,ef core的addrange在oracle上仍是逐条insert;真正高效方案是使用oracle.manageddataaccess内置的oraclebulkcopy类,需绕过ef core上下文直连数据库并满足并行dml等条件。
oracle不支持sqlbulkcopy,ef core原生addrange没用
ef core 8 的 addrange + savechanges 在 oracle 上仍是逐条 insert,底层走的是标准 sql 协议,不会触发任何数据库级批量机制。你看到的“批量插入”只是客户端把多条语句打包进一次事务提交,但每条 insert 仍独立解析、编译、执行——网络往返没减少,日志写入和索引维护开销照旧。实测插入 5000 条记录,耗时通常是原生 oracle insert /*+ append */ 的 8–10 倍。
必须用 Oracle.ManagedDataAccess + OracleBulkCopy
Oracle 官方驱动(Oracle.ManagedDataAccess v23.x+)已内置 OracleBulkCopy 类,这是唯一能真正绕过 SQL 解析、直连 Oracle 高速加载通道的方式。它对标 SQL Server 的 SqlBulkCopy,要求目标表启用 ENABLE PARALLEL DML 和合适的存储参数(如 NOLOGGING 可选),且不走 EF Core 的上下文管道。
- 不能用
DbContext实例调用,得新建OracleConnection并保持打开状态 - 源数据必须是
DataTable、IDataReader或实现了IBulkCopyTable的类型;实体类需先映射为DataTable - 列名、顺序、数据类型必须与目标表严格一致,Oracle 对大小写敏感(默认大写),
DataTable.Columns.Add("ID")和表字段"id"会失败 - 不支持外键约束校验、触发器、默认值生成(如序列
NEXTVAL),得提前在DataTable中填好主键值
EFCore.BulkExtensions 对 Oracle 支持有限
EFCore.BulkExtensions v8.x 默认只适配 SQL Server、PostgreSQL 和 SQLite。虽然它声称“跨数据库”,但 Oracle 后端实际调用的是模拟的多值 INSERT(INSERT INTO t VALUES (...), (...), ...),受 Oracle 12c+ 的 INSERT ALL 语法限制:单次最多 1000 行,且无法利用直接路径加载(Direct Path)。更麻烦的是,它依赖 Oracle.EntityFrameworkCore 提供的方言扩展,而该 provider 目前(2026 年中)尚未实现 BulkInsert 的原生 Oracle 优化路径。
- 若强行调用
context.BulkInsert(entities),底层会退化为分批INSERT ALL,每批 1000 行,仍产生大量 round-trip - 不兼容达梦、人大金仓等国产 Oracle 兼容库,那些库的 bulk 接口完全不同
- 事务控制受限:
OracleBulkCopy必须在显式事务内使用,而BulkExtensions的transaction参数对 Oracle 无效
手动拼接 INSERT ALL 是折中方案
当无法用 OracleBulkCopy(比如权限受限、表有触发器),又不愿引入商业库时,可手写分批 INSERT ALL。EF Core 8 的 ExecuteSqlRaw 能安全注入参数,但注意 Oracle 绑定变量上限是 1000 个,所以每批最多 333 行(每行 3 列)。
示例:将 List<product></product> 转成 300 行一批的 INSERT ALL
INSERT ALL INTO products (id, name, price) VALUES (:p0_id, :p0_name, :p0_price) INTO products (id, name, price) VALUES (:p1_id, :p1_name, :p1_price) -- ... up to 300 rows SELECT 1 FROM DUAL
- 必须用
OracleParameter数组传参,不能用匿名对象或字典,否则类型推导错误 -
SELECT 1 FROM DUAL是 Oracle 语法必需,漏掉会报ORA-00928: missing SELECT keyword - 避免字符串拼接列名或表名,防止 SQL 注入;所有标识符应通过
context.Database.GetDbConnection()获取的连接验证
Oracle 批量插入真正的瓶颈不在 C# 代码怎么写,而在是否绕开了 SQL 解析层——只要还走 OracleCommand.ExecuteNonQuery,就注定慢。OracleBulkCopy 是目前唯一解,但它要求你暂时放下 EF Core 的抽象,直面 Oracle 的物理加载契约。











