這次介紹的這個框架只適用於中小項目,並且各個讀寫數據庫結構是一致的情況,還要並且是寫入 數據庫只有1台情況。
我們來看看這個子系統適用的場景:
我們來看這個子系統的配置文件:
<?xml version="1.0" encoding="utf-8" ?> <SQLDispatcher> <WritableDB>Server=.;Database=d1;User Id=sa;Password=111111;</WritableDB> //唯一的主數據庫(寫入DB) <ReadDBs> <DB>Server=.;Database=d2;User Id=sa;Password=111111;</DB> //這些是普通的對等的讀數據庫,只是做了些普通索引優化 <DB>Server=.;Database=d3;User Id=sa;Password=111111;</DB> //同上 <DB>Server=.;Database=d4;User Id=sa;Password=111111;</DB> //同上 </ReadDBs> <DedicatedReadDBs> <DedicatedRegion> <Region>Optimization_Sales</Region> //這個區域代表所列出來的DB是專門針對銷售報表優化索引的數據庫 <DB>Server=.;Database=d5;User Id=sa;Password=111111;</DB> <DB>Server=.;Database=d6;User Id=sa;Password=111111;</DB> </DedicatedRegion> <DedicatedRegion> <Region>Optimization_HR</Region> //這樣的專門Region可以有多個區域 <DB>Server=.;Database=d7;User Id=sa;Password=111111;</DB> </DedicatedRegion> </DedicatedReadDBs> </SQLDispatcher>
上述配置文件的讀取,略。
業務層中,可以做到這樣的寫法:
[AOPServiceEnabled()] //這句是為了和AOP代理掛鉤 public class OrderQueryService : OrderQueryServiceInterface { IOrderQueryServiceDataProvider dp = new OrderQueryServiceSqlDataProvider(); public override QueryResult<QueryDto.OrderDto> QueryByFirstName(string firstName, PagingInfo pgInfo) { //這個函數沒有加SQLDispatcher標記,系統會自己選擇sql連接(寫入sql:就那1個;讀取sql:從ReadDBs中取模選中1個) QueryResult<QueryDto.OrderDto> lst=dp.QueryByFirstName(firstName, pgInfo); foreach (OrderDto o in lst.List) o.FirstName += DateTime.Now.ToString(); return lst; } [SQLDispatcher("Optimization_Sales")] //顯式指定sql語句走 Optimization_Sales區域 public override QueryResult<QueryDto.OrderDto> QueryByEmail(string email) { QueryResult<QueryDto.OrderDto> lst = dp.QueryByEmail(email); return lst; } }
我們來看下UML:
SQLDispatcherContext用於保存當前函數的Region,這裡保存的數據是瞬間的,隨著函數的開始執 行而有數據,隨著函數的結束而被reset。
DBSelector是核心算法,用於根據配置文件算出不同的可選db,代碼如下
public class DBSelector { public static DB SelectDB(string sql, string region) { bool redirect2WritableDB = false; sql = sql.Trim().TrimStart('\r').TrimStart('\n'); if (sql.IndexOf("UPDATE", StringComparison.OrdinalIgnoreCase) >= 0) redirect2WritableDB = true; if (sql.IndexOf("DELETE", StringComparison.OrdinalIgnoreCase) >= 0) redirect2WritableDB = true; if (sql.IndexOf("INSERT", StringComparison.OrdinalIgnoreCase) >= 0) redirect2WritableDB = true; if (sql.IndexOf("--WRITE", StringComparison.OrdinalIgnoreCase) == 0) //強制sql方式進入寫db操作 redirect2WritableDB = true; if (redirect2WritableDB) return Config.SQLDispatcherConfiguration.WritableDB; if (region == null || region.Length == 0) //from normal read dbs { int random = GenerateRandomNumber(); int dbIndex = random % Config.SQLDispatcherConfiguration.ReadDBs.Count; return Config.SQLDispatcherConfiguration.ReadDBs[dbIndex]; } DedicatedRegion r = Config.SQLDispatcherConfiguration.DedicatedRegions.Find(t => t.Region.Equals(region, StringComparison.OrdinalIgnoreCase)); if (r == null) throw new Exception("No such Dedicated Region Identifier."); { int random = GenerateRandomNumber(); int dbIndex = random % r.DBs.Count; return r.DBs[dbIndex]; } } private static int GenerateRandomNumber() { Random Random1 = new Random(); //產生0到1000的隨機數 int i1 = Random1.Next(0, 1001); return i1; } }
SqlHelperCoordinator類只是簡單的根據DBSelector算出的結果調度真正的SqlHelper來執行:
public sealed class SqlHelperCoordinator { public static int ExecuteNonQuery(CommandType commandType, string commandText, params SqlParameter[] commandParameters) { string region = SQLDispatcherContext.GetCurrentContext().Region; DB db=DBSelector.SelectDB(commandText, region); return SqlHelper.ExecuteNonQuery(db.ConnectionString, commandType, commandText, commandParameters); } public static SqlDataReader ExecuteReader(CommandType commandType, string commandText, params SqlParameter[] commandParameters) { string region = SQLDispatcherContext.GetCurrentContext().Region; DB db = DBSelector.SelectDB(commandText, region); return SqlHelper.ExecuteReader(db.ConnectionString, commandType, commandText, commandParameters); } }
查看本欄目
Console測試代碼(記得打開Sql profile檢測sql哦):
static void Main(string[] args) { InstancePoolResolver.Register<OrderQueryServiceInterface, OrderQueryService>(); using (OrderQueryServiceInterface srv = InstancePoolResolver.Resolve<OrderQueryServiceInterface>()) { while (true) { //Thread.Sleep(1000); Console.ReadKey(); QueryResult<Core.QueryService.QueryDto.OrderDto> lst=srv.QueryByFirstName("aaron", new CoreFramework.QueryService.PagingInfo() { PageIndex = 0, PageSize = 10, OrderByColumn = "FirstName", IsAscendingSort = true }); lst.List.ForEach(t=>Console.WriteLine(t.FirstName)); srv.QueryByEmail("aaron"); } } }
運行2次(關閉後再運行,因為緩存還沒有好,bug)
就會看到:
被查詢的數據庫名正好落在xml配置文件的范圍
代碼下載:http://files.cnblogs.com/aarond/Cache_2.rar