zoukankan      html  css  js  c++  java
  • 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中利用拦截器等效,至于更加复杂的逻辑可自行实现。

  • 相关阅读:
    Bootstrap_让Bootstrap轮播插件carousel支持左右滑动手势的三种方法
    JAVA_用Java来获取访问者真实的IP地址
    Html5_移动前端不得不了解的html5 head 头标签
    ThinkPad_T430重装系统
    JavaScript_JS判断客户端是否是iOS或者Android
    Html5_禁止Html5在手机上屏幕页面缩放
    HttpClient_httpclient 4.3.1 post get的工具类
    HttpClient_使用httpclient必须知道的参数设置及代码写法、存在的风险
    LATEX数学公式基本语法
    为WLW开发Latex公式插件
  • 原文地址:https://www.cnblogs.com/CreateMyself/p/9261435.html
Copyright © 2011-2022 走看看