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


前言

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

EF Core讀寫分離進一步完善

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

    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中進行注冊該監聽類。

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

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

        [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中利用攔截器等效,至於更加復雜的邏輯可自行實現。


免責聲明!

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



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