Server=.;Database=d1;Uid=sa;Pwd=111111 Server=.;Database=d2;Uid=sa;Pwd=111111 Server=.;Database=d3;Uid=sa;Pwd=111111 Optimization_Sales Server=.;Database=d5;Uid=sa;Pwd=111111
[AOPServiceEnabled]
public class OrderQueryService : ContextBoundObject, IOrderQueryService {
[SQLDispatcher] // 默認路由:寫主庫、讀普通從庫
public QueryResult QueryByFirstName(string name, PagingInfo pg) { ... }
[SQLDispatcher("Optimization_Sales")] // 顯式指定專用讀庫區域
public QueryResult QueryByEmail(string email) { ... }
}
public class DBSelector {
public static DB SelectDB(string sql, string region) {
// 1. 判斷寫操作
bool write = sql.TrimStart().StartsWith("--WRITE", StringComparison.OrdinalIgnoreCase)
|| Regex.IsMatch(sql, @"^\s*(INSERT|UPDATE|DELETE)", RegexOptions.IgnoreCase);
if (write) return Config.WritableDB;
// 2. 讀操作路由
if (string.IsNullOrEmpty(region)) {
int idx = Random.Shared.Next() % Config.ReadDBs.Count;
return Config.ReadDBs[idx];
}
var dedicated = Config.DedicatedRegions.FirstOrDefault(r => r.Region.Equals(region, StringComparison.OrdinalIgnoreCase));
if (dedicated == null) throw new Exception($"Unknown region: {region}");
int idx2 = Random.Shared.Next() % dedicated.DBs.Count;
return dedicated.DBs[idx2];
}
}
public sealed class SqlHelperCoordinator {
public static int ExecuteNonQuery(...) {
var db = DBSelector.SelectDB(sql, SQLDispatcherContext.Current.Region);
return SqlHelper.ExecuteNonQuery(db.ConnectionString, ...);
}
public static SqlDataReader ExecuteReader(...) { /* 同理 */ }
}
SQLDispatcherContext 由 AOP 攔截器在方法調用前設置 Region,調用後清除,保證線程安全。
啟動 SQL Profiler 監控,調用兩個查詢方法,可見連接分別落在配置的不同庫上。