Oracle.ManagedDataAccess不支持ReadOnly=True参数,必须用两个独立连接字符串分别指向主库和只读副本,并通过IReadDbContext/IWriteDbContext显式路由;EF Core需在实例化阶段分离读写,Dapper+连接工厂更轻量可控。

Oracle.ManagedDataAccess 不支持 ReadOnly=True 参数
直接在连接字符串里加 ReadOnly=True 没用——Oracle 官方驱动完全忽略这个参数,既不报错也不生效。写操作照样能发到标为“只读”的库上,主库压力一点没降。这不是配置问题,是 Oracle 驱动本身就不提供 SQL Server 那种 ApplicationIntent=ReadOnly 的协商机制。
必须用两个独立连接字符串 + 显式路由
真正起作用的只有两件事:一个指向主库的连接串(DataSource=ORCL_WRITE),一个指向只读副本的连接串(DataSource=ORCL_READ),且两者不能共用同一个 OracleConnectionStringBuilder 实例去动态改 DataSource。常见错误是复用对象后调 .DataSource = "xxx",结果连接池混用、事务跨库失败。
- 读库连接串里务必显式设置
Pooling=false,否则连接可能被复用到写操作中 - 两个连接串的
User Id、Password、Connection Timeout应保持一致,仅DataSource不同 - 确保
tnsnames.ora或连接串中使用的服务名已正确定义,否则会报ORA-12154
EF Core 中不要在 OnConfiguring 里动态切换连接串
在 DbContext.OnConfiguring 里根据当前请求类型拼接不同连接串,会导致连接池混乱、SaveChanges() 可能意外走读库、跨上下文事务失效。EF Core 没有内置读写分离中间件,必须把路由逻辑提前到 DbContext 实例化阶段。
- 推荐注册两个服务:
IReadDbContext和IWriteDbContext,分别绑定不同连接串构建的DbContextOptions - 仓储方法里按需注入对应接口,比如查询用
IReadDbContext,Add/Update/Remove操作用IWriteDbContext - 强一致性场景(如刚写完立刻查)必须强制走写库,不能依赖从库同步延迟
Dapper + 连接工厂是最轻量可控的方案
如果项目没重度依赖 EF Core,Dapper 配合手工管理的连接工厂反而更清晰、更容易调试。核心就是一个 OracleConnectionFactory 类,根据 ConnectionRole.Read 或 ConnectionRole.Write 返回对应连接。
- 工厂内部用两个独立的连接字符串字段初始化,不共享状态
- 每次
Create()都新建OracleConnection,避免连接复用风险 - 读操作用
Asynchronous=true和FetchSize控制批量读取效率,写操作注意Enlist=false避免隐式参与分布式事务


















