Oracle.ManagedDataAccess不支持ReadOnly=true,需用两个独立连接字符串区分读写库;读库连接必须禁用连接池(Pooling=false),并由工厂类按角色返回对应连接,EF Core需定义两个DbContext类型分别处理读写操作。
Oracle.ManagedDataAccess 不认 ReadOnly=True
直接在连接字符串里加 readonly=true 没用。oracle 官方驱动完全忽略这个参数,写操作照样执行,连接也不会自动路由到只读副本。这不是配置漏了,是机制上就不支持——sql server 的 applicationintent=readonly 在 oracle 里没有对应物。
常见错误现象:ORA-01017 或 ORA-12154,往往是因为读库连接串里误用了主库的 service_name,或 TNS 别名没在 tnsnames.ora 中正确定义。
- 必须准备两个独立连接字符串:一个指向主库(如
Data Source=ORCL_PRIM),一个指向只读备库(如Data Source=ORCL_STBY) - 两个字符串中除
DataSource外,User Id、Password、Connection Timeout应保持一致 - 不要复用同一个
OracleConnectionStringBuilder实例后修改DataSource——这会导致连接池污染
连接工厂必须区分读/写角色并禁用读库连接池
核心是让每次获取连接时,明确知道该连主库还是只读库。最简方式是封装一个工厂类,根据枚举参数返回对应连接:
public enum ConnectionRole { Read, Write }
public class OracleConnectionFactory
{
private readonly string _readConnString;
private readonly string _writeConnString;
public OracleConnectionFactory(string readConn, string writeConn)
{
_readConnString = readConn;
_writeConnString = writeConn;
}
public OracleConnection Create(ConnectionRole role) =>
new(role == ConnectionRole.Read ? _readConnString : _writeConnString);
}
关键细节:
- 读库连接字符串中务必设置
Pooled=false(或显式Pooling=false),否则连接池可能把写操作复用到读库连接上 - 主库连接建议启用
Validate Connection=true,防止连接池复用失效连接;只读库可关掉,减少心跳开销 - 若用 Dapper,直接传入工厂返回的连接:
connection.Query<T>(...),不走全局连接池
EF Core 中不能靠 OnConfiguring 动态切换
在 DbContext.OnConfiguring 里根据当前方法名或 SQL 内容动态改连接字符串,会导致连接池混乱、事务跨库失败、并发下连接错配。EF Core 的 DbContext 实例不是线程安全的,更不是连接路由器。
可行做法:
- 定义两个
DbContext类型:WriteDbContext和ReadDbContext,各自注入固定连接字符串 - 对
SaveChanges、ExecuteSqlRaw等写操作,强制使用WriteDbContext - 查询时优先用
ReadDbContext.AsNoTracking(),但需业务层显式控制——不能依赖 ORM 自动识别 - 事务内(
TransactionScope)所有操作必须绑定同一连接,此时应自动降级为全走主库,否则抛OracleException:“distributed transaction not supported”
只读库宕机时得自己兜底重试
Oracle 没有客户端自动重定向机制。当 ORCL_STBY 不可用时,OracleConnection.Open() 会直接抛 ORA-12170: TNS:Connect timeout occurred,不会 fallback 到主库。
必须在连接工厂里补全逻辑:
- 加入指数退避重试(最多 2 次),首次失败后快速降级到主库连接
- 健康检查不能只 ping 连接,要执行
SELECT 1 FROM DUAL并设短超时(如 2s) - 避免用 SQL 文本解析判断读写——
SELECT COUNT(*) FROM (UPDATE ...)这类嵌套写法会让正则误判
真正麻烦的不是怎么连两个库,而是主从延迟感知、事务一致性边界、故障时的语义降级——这些都得自己写,Oracle 一概不管。


















