EntityFramework Core進行讀寫分離最佳實踐方式,瞭解一下(二)?

前言

寫過上一篇關於EF Core中讀寫分離最佳實踐方式後,雖然在必定程度上改善了問題,可是在評論中有的指出更換到從數據庫,那麼接下來要進行插入此時又要切換到主數據庫,同時有的指出是否能夠進行底層無感知操做,這確實是個問題,本文繼續進行引路,進一步改善評論中問題的指出,至於實現更復雜的邏輯可自行實現,感謝大家很是棒的評論,使得本文由此而生。git

EF Core讀寫分離進一步完善

如評論前輩們指出在EF 6.x中咱們知道有IDbCommandInterceptor接口,咱們可對執行的SQL語句進行攔截,從而可自定義實現咱們想要的需求,雖然在EF Core中卻沒有攔截器特性的實現,可是針對此特性的實現DiagnosticSource記錄跟蹤類來實現等效於攔截器的實現,當前DiagnosticSource使用文檔還沒有完善,估計還得等待一段時間,接下來咱們來看看如何實現。在DiagnosticSource包中有DiagnosticListener類,咱們經過此類來跟蹤記錄,若是執行的EF Core包,那麼咱們將利用DiagnosticListener進行訂閱,訂閱到以後咱們拿到跟蹤命令,從而實現無感知更換數據庫,代碼以下:github

    public class DbCommandInterceptor : IObserver<KeyValuePair<string, object>>
    {
        private const string masterConnectionString = "data source=WANGPENG;User Id=sa;Pwd=sa123;initial catalog=Demo1;integrated security=True;";
        private const string slaveConnectionString = "data source=WANGPENG;User Id=sa;Pwd=sa123;initial catalog=Demo2;integrated security=True;";
        public void OnCompleted()
        {
        }

        public void OnError(Exception error)
        {
        }

        public void OnNext(KeyValuePair<string, object> value)
        {
            if (value.Key == RelationalEventId.CommandExecuting.Name)
            {
                var command = ((CommandEventData)value.Value).Command;
                var executeMethod = ((CommandEventData)value.Value).ExecuteMethod;

                if (executeMethod == DbCommandMethod.ExecuteNonQuery)
                {
                    ResetConnection(command, masterConnectionString);
                }
                else if (executeMethod == DbCommandMethod.ExecuteScalar)
                {
                    ResetConnection(command, slaveConnectionString);
                }
                else if (executeMethod == DbCommandMethod.ExecuteReader)
                {
                    ResetConnection(command, slaveConnectionString);
                }
            }
        }

        void ResetConnection(DbCommand command, string connectionString)
        {
            if (command.Connection.State == ConnectionState.Open)
            {
                if (!command.CommandText.Contains("@@ROWCOUNT"))
                {
                    command.Connection.Close();
                    command.Connection.ConnectionString = connectionString;
                }
            }
            if (command.Connection.State == ConnectionState.Closed)
            {
                command.Connection.Open();
            }
        }
    }

這裏存在一個問題,上述 command.CommandText.Contains("@@ROWCOUNT") 代碼,由於在進行添加操做時,會返回主鍵,那麼此時會進行查詢,因此暫時沒有更好的方式是確認主-從數據庫。數據庫

    public class CommandListener : IObserver<DiagnosticListener>
    {
        private readonly DbCommandInterceptor _dbCommandInterceptor = new DbCommandInterceptor();

        public void OnCompleted()
        {
        }

        public void OnError(Exception error)
        {
        }

        public void OnNext(DiagnosticListener listener)
        { 
            if (listener.Name == DbLoggerCategory.Name)
            {
                listener.Subscribe(_dbCommandInterceptor);
            }
        }
    }

上述 DbLoggerCategory.Name 也就是 Microsoft.EntityFrameworkCore ,經過監控的包是Microsoft.EntityFrameworkCore,則進行訂閱,最後咱們在startup中進行註冊該監聽類。spa

 DiagnosticListener.AllListeners.Subscribe(new CommandListener());

接下來咱們經過動態圖來看看最終實際效果,主-從複製依然是經過SQL Server發佈-訂閱的方式來同步數據。在控制器中,咱們只利用demo1上下文來添加和查詢數據,當查詢時更換到從數據庫,此時已經是無感知(請見上一篇),以下:code

        [HttpGet("index")]
        public IActionResult Index()
        {
            var blogs = _demo1DbContext.Blogs.ToList();
            return View(blogs);
        }

        [HttpGet("create")]
        public IActionResult CreateDemo1Blog()
        {
            var blog = new Blog()
            {
                Name = "demoBlog1",
                Url = "http://www.cnblogs.com/createmyslef"
            };
            _demo1DbContext.Blogs.Add(blog);
            _demo1DbContext.SaveChanges();
            return RedirectToAction(nameof(Index));
        }

 

總結

本文只是在上一篇的基礎上進一步改善了讀寫分離的操做,得益於github上有提出可經過跟蹤記錄來解決,經過跟蹤記錄從底層出發來完善讀寫分離操做,咱們可拿到底層實現的命令以及其餘和EF 6.x中利用攔截器等效,至於更加複雜的邏輯可自行實現。server

相關文章
相關標籤/搜索