這次介紹的這個框架只適用於中小項目,並且各個讀寫數據庫結構是一致的情況,還要並且是寫入 數據庫只有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