必须用两个独立连接字符串+显式路由,因onconfiguring仅执行一次且连接池无法区分读写角色,复用会导致ora-12154、强一致性破坏及连接混用;读库须pooling=false、专用tns别名、禁用windows认证。

必须用两个独立连接字符串 + 显式路由,不能靠动态切换或单个 OracleConnectionStringBuilder 实例改写 DataSource。
为什么不能在 DbContext.OnConfiguring 里动态换连接串
EF Core 的 DbContext.OnConfiguring 是单例生命周期内只执行一次的配置入口。一旦注册了连接字符串,后续所有 DbContext 实例都会复用该配置——哪怕你在方法里加 if 判断,也只会按首次调用时的值固定下来。更严重的是,连接池会把读库和写库的连接混在一起管理,导致:
- 事务中执行
SELECT可能意外落到从库(违反强一致性) - 从库连接被写操作复用,触发 ORA-12154 或 ORA-01017(因从库 TNS 别名没配写权限或 service_name 不匹配)
- 连接池无法区分角色,
Min Pool Size=1时低峰期回收所有读连接,高峰期重建开销陡增
读库连接串必须关闭连接池并指向正确 TNS 别名
读库不是“写库连上后切只读”,而是物理上连到另一台 Oracle 实例(如 ORCL_READ)。关键配置项有三个:
-
Data Source必须是只读副本的 TNS 别名(如(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=oraread01)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=ORCL_RO)))),不能复用主库别名 -
Pooling=false—— 否则读库连接可能被写操作复用;即使设Pooling=true,也得确保Connection Lifetime=0避免跨库复用 - 避免使用
Integrated Security=true或 Windows 身份认证,从库通常禁用该模式
用工厂类封装连接创建逻辑(Dapper / ADO.NET 场景)
不依赖 ORM 的自动路由,直接控制连接来源最可靠。示例结构如下:
public enum ConnectionRole { Read, Write }
public class OracleConnectionFactory
{
private readonly string _readConnString;
private readonly string _writeConnString;
public OracleConnectionFactory(string readConn, string writeConn)
{
_readConnString = readConn; // Pooling=false; Data Source=ORCL_READ
_writeConnString = writeConn; // Pooling=true; Data Source=ORCL_WRITE
}
public OracleConnection Create(ConnectionRole role)
{
var connStr = role == ConnectionRole.Read ? _readConnString : _writeConnString;
var conn = new OracleConnection(connStr);
conn.Open(); // 立即打开,避免后续调用时才暴露连接问题
return conn;
}
}
调用时显式指定角色:
- 查列表、统计、报表:用
factory.Create(ConnectionRole.Read) - INSERT/UPDATE/DELETE/带 SELECT FOR UPDATE:必须用
ConnectionRole.Write - 事务内所有操作必须走同一连接,因此整个
using块只能用一个Create()结果
SQLSugar 中如何安全启用只读路由
SQLSugar 支持 Ado.UseCommand 和 Ado.UseConnection 手动接管连接,比 EF Core 更适合读写分离:
- 注册时不要传默认连接串,改用工厂委托:
sugar.Register(new ConnectionFactory().Create) - 查询方法统一加
.Ado.UseConnection(conn),其中conn来自factory.Create(ConnectionRole.Read) - 写操作仍走默认上下文,但需确保其内部连接串是写库地址且
Pooling=true - 注意
DbType.Oracle必须显式指定,否则 SQLSugar 可能误判参数类型导致 ORA-01008
真正难的不是连上从库,而是让每个 SQL 请求都明确知道自己该走哪条路——没有框架能自动判断“这个 SELECT 是否允许延迟”。路由逻辑一旦漏掉一个地方,强一致场景就崩了。











