基於 EntityFramework 的數據庫主從讀寫分離架構(1) - 原理概述和基本功能實現


     回到目錄,完整代碼請查看https://github.com/cjw0511/NDF.Infrastructure)中的目錄:
     src\ NDF.Data.EntityFramework\MasterSlaves
 
    在本上的上一篇博文中(基於 EntityFramework 的數據庫主從讀寫分離服務插
件, http://www.cnblogs.com/cjw0511/p/4391092.html),概述性的介紹了自己基於 EF6 寫的一個用於數據庫主從讀寫分離服務的一個插件。因為時間關系,上一篇博文只講到了該插件的功能概述和基本用法。今天正好有空,就花點時間構思了這篇博文,和大家一起來交流一下,本人基於 EF6 的數據庫主從讀寫分離服務插件是如何構思、設計以及編碼實現的。
 
    首先,在繼續進行本文的后續內容閱讀之前,應該先對一些相關知識點有所了解,包括但不限於如下幾個方面:
    1、ADO.NET 基礎知識;
    2、EntityFramework 的功能和基本用法;
    3、數據庫讀寫分離的概念( http://baike.baidu.com/view/3372624.htm);
    4、數據庫主從復制功能的基本配置(不同數據庫系統配置方式不同);
    以上幾點是閱讀和實現本文所述的數據庫主從讀寫分離的基礎知識,本文就不做詳細贅述了,如有想在這些方面另需了解的同學,請自行補課。
 
    好了,接下來進入正題吧。
    我們都知道,在應用程序開發中,要實現數據庫的主從讀寫分離操作,也就是讓所有的數據變更操作請求都指向 Master(主) 服務器,而所有的數據查詢請求都指向 Slave(從)服務器,其根本就在於構建 ADO.NET 的 DbConnection 連接時,ConnectionString 的指向不同。在以前,沒有用 ORM 框架時,我們可能通過自定義一些諸如 DataAccessHelper 的工具類,在工具類型定義兩大類型的 API,其一為數據庫變更操作,其二為數據查詢操作。在數據庫變更操作中,生成的 DbConnection 的連接地址指向 Master 服務器;在數據查詢操作時,生成的 DbConnection 指向另一台 Slave 服務器。
 
    而如今,由於需要分離關注點、提高代碼復用性和編碼工作效率、降低程序員對 sql 的知識依賴等諸多原因,我們用上了 EntityFramework、NHibernate 等類似的 ORM 框架。實際上,在 ORM 框架中,要實現數據庫讀寫分離,和我們早期直接用 ADO.NET 來實現該功能的原理是一樣的。而不同之處,主要就在於,ORM 框架一般提供了AOP 架構的切面注入方式,讓我們可以在某個關注點上添加我們自定義的操作。
 
    在 EntityFramework 的 6.1 版本中,新增了一個命名空間 System.Data.Entity.Infrastructure.Interception,該命名空間主要就是提供了一個面向切面的程序注入功能,讓我們可以在 EF 最終向數據庫提交 DbCommand 執行請求前后,攔截到該動作並執行我們自定義的其他附加動作。基本的做法為:
    1、自定義一個 System.Data.Entity.Infrastructure.Interception.DbCommandInterceptor 類型的子類;
    2、在該自定義類型中重寫方法 ReaderExecuting、ReaderExecuted、ScalarExecuting、ScalarExecuted、NonQueryExecuting、NonQueryExecuted,方法體內即可實現自己對查詢命令、更改命令的執行前后附加動作;
    3、通過 System.Data.Entity.Infrastructure.Interception.DbInterception.Add 方法,將該自定義的類型實例添加至 EF 執行上下文中;
    完成以上幾個步驟后,EF 即可在每次執行增刪改或查詢命令前后,額外我們我們定義的其他動作。
 
    廢話不多說,下面貼上一段代碼,用於表示實現 EF 讀寫分離效果的 DbCommandInterceptor 基本實現:
 1 public class DbMasterSlaveCommandInterceptor : DbCommandInterceptor
 2     {
 3         private string masterConnectionString = "server=192.168.0.99;port=3306;user id=root;password=123456;persistsecurityinfo=True;database=testdb";
 4         private string slaveConnectionString = "server=192.168.0.101;port=3306;user id=root;password=123456;persistsecurityinfo=True;database=testdb";
 5         public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
 6         {
 7             this.UpdateConnectionString(interceptionContext, this.slaveConnectionString);
 8         }
 9         public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
10         {
11             this.UpdateConnectionString(interceptionContext, this.slaveConnectionString);
12         }
13         public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
14         {
15             this.UpdateConnectionString(interceptionContext, this.masterConnectionString);
16         }
17         private void UpdateConnectionString(DbInterceptionContext interceptionContext, string connectionString)
18         {
19             foreach (var context in interceptionContext.DbContexts)
20             {
21                 this.UpdateConnectionString(context.Database.Connection, connectionString);
22             }
23         }
24         private void UpdateConnectionString(DbConnection conn, string connectionString)
25         {
26             ConnectionState state = conn.State;
27             if (state == ConnectionState.Open)
28                 conn.Close();
29             conn.ConnectionString = connectionString;
30             if (state == ConnectionState.Open)
31                 conn.Open();
32         }
33     }

 

    接着,在 Global.asax 的啟動代碼中將該 類型的實體注入 EF 全局執行上下文中。

 

1 public class MyHttpApplication : HttpApplication
2     {
3         protected void Application_Start()
4         {
5             DbInterception.Add(new DbMasterSlaveCommandInterceptor());
6         }
7     }

 

    怎么樣,原理是不是很簡單?當然,如果想要實現一些豐富的配置和擴展功能,就還需要很多其他的代碼了,關於這些本人將會在后續文章中逐步介紹!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM