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

为了方便大家在移动端也能看到我分享的博文,现已注册个人公众号,扫描上方左边二维码即可,欢迎大家关注,有时间会及时分享相关技术博文。
感谢花时间阅读此篇文章,如果您觉得这篇文章你学到了东西也是为了犒劳下博主的码字不易不妨打赏一下吧,让楼主能喝上一杯咖啡,在此谢过了!
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!
本文版权归作者和博客园共有,来源网址:http://www.cnblogs.com/CreateMyself)/欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构