程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> 讀寫分離子系統:C# SQL分發子系統(目前只支持ADO.NET)

讀寫分離子系統:C# SQL分發子系統(目前只支持ADO.NET)

編輯:關於C#

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved