日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

EF Core3.0+ 通过拦截器实现读写分离与SQL日志记录

發(fā)布時間:2023/12/4 数据库 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 EF Core3.0+ 通过拦截器实现读写分离与SQL日志记录 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

本文主要是講解EF Core3.0+ 通過攔截器實現(xiàn)讀寫分離與SQL日志記錄

注意攔截器只有EF Core3.0+ 支持,2.1請考慮上下文工廠的形式實現(xiàn).

說點題外話..

一晃又大半年沒更新技術(shù)博客..唉,去年一年發(fā)生了太多事情..博主真的 一言難盡..

有興趣的可以去看看:記錄一下,也許是轉(zhuǎn)折,也許是結(jié)束,也許是新希望的一年

?

正文

1.通過攔截器實現(xiàn)讀寫分離

先講一下本文實現(xiàn)的方式吧

SQL 通過數(shù)據(jù)庫本身的功能 實現(xiàn)主從備份 大概原理如圖:

?

?

?

?

EF Core在查詢的時候通過DbCommandInterceptor 攔截器(PS:這個功能在EF6.0+中也實現(xiàn)了)來攔截對數(shù)據(jù)庫的訪問,從而切換主從數(shù)據(jù)庫

下面直接上代碼吧

首先我們創(chuàng)建一個類 繼承DbCommandInterceptor:

?public class DbMasterSlaveCommandInterceptor : DbCommandInterceptor

{private string _masterConnectionString;private string _slaveConnectionString;public DbMasterSlaveCommandInterceptor(string masterConnectionString, string slaveConnectionString){_masterConnectionString = masterConnectionString;_slaveConnectionString = slaveConnectionString;} }

通過構(gòu)造函數(shù)傳遞主庫連接地址與從庫地址(可有多個 通過"|"分割)

添加一個隨機分配從表讀取連接的方法(PS:這里只是demo所以很簡陋的隨機,如果正式要用,應包含權(quán)重判斷,定時心跳從庫連接情況,請自行修改):

?    /// <summary>

/// 通過隨機數(shù)分配獲取多個從庫/// </summary>/// <returns></returns>private string GetSlaveConnectionString(){var readArr = _slaveConnectionString.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);var resultConn = string.Empty;if (readArr != null && readArr.Any()){resultConn = readArr[Convert.ToInt32(Math.Floor((double)new Random().Next(0, readArr.Length)))];}return resultConn;}

添加判斷是否主從操作連接方法:

? ? ? ? private void UpdateToSlave(DbCommand command)

{//判斷是否配置了主從分離if (!string.IsNullOrWhiteSpace(GetSlaveConnectionString()))//如果配置了讀寫分離,就進入判斷{//判斷是否為插入語句(EF 插入語句會通過Reader執(zhí)行并查詢主鍵),否則進入if (command.CommandText.ToLower().StartsWith("insert", StringComparison.InvariantCultureIgnoreCase) == false){// 判斷當前會話是否處于分布式事務中bool isDistributedTran = Transaction.Current != null &&Transaction.Current.TransactionInformation.Status !=TransactionStatus.Committed;//判斷該 context 是否處于普通數(shù)據(jù)庫事務中bool isDbTran = command.Transaction != null;//如果不處于事務中,則執(zhí)行從服務器查詢if (!isDbTran && !isDistributedTran){command.Connection.Close();command.Connection.ConnectionString = GetSlaveConnectionString();command.Connection.Open();}}}}

?

重載DbCommandInterceptor當中的攔截方法,代碼如下:

//如果是寫入,則正常執(zhí)行public override InterceptionResult<int> NonQueryExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<int> result){return base.NonQueryExecuting(command, eventData, result);}public override Task<InterceptionResult<int>> NonQueryExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default){return base.NonQueryExecutingAsync(command, eventData, result, cancellationToken);}public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result){this.UpdateToSlave(command);return base.ReaderExecuting(command, eventData, result);}public override Task<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result, CancellationToken cancellationToken = default){this.UpdateToSlave(command);return base.ReaderExecutingAsync(command, eventData, result, cancellationToken);}public override InterceptionResult<object> ScalarExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<object> result){this.UpdateToSlave(command);return base.ScalarExecuting(command, eventData, result);}public override Task<InterceptionResult<object>> ScalarExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<object> result, CancellationToken cancellationToken = default){this.UpdateToSlave(command);return base.ScalarExecutingAsync(command, eventData, result, cancellationToken);}

最后在EF core的上下文中注入攔截器(PS:我這里使用的Autofac模塊注入):

builder.Register(c =>{var optionsBuilder = new DbContextOptionsBuilder<TestEFContext>();//注入攔截器optionsBuilder.AddInterceptors(new DbMasterSlaveCommandInterceptor(WriteConnect, ReadConnect));//MaxBatchSize 處理批量操作BUGoptionsBuilder.UseMysql(WriteConnect, b=>b.MaxBatchSize(1));return optionsBuilder.Options;}).As<DbContextOptions<TestEFContex>>().SingleInstance();

這樣就實現(xiàn)了通過攔截器實現(xiàn)讀寫分離.

?

2.通過攔截器實現(xiàn)SQL日志記錄

同理,我們可以通過攔截器實現(xiàn)EF Core SQL語句的記錄與調(diào)試

首先我們創(chuàng)建一個新的攔截器DBlogCommandInterceptor 如下:

public class DBlogCommandInterceptor : DbCommandInterceptor{//創(chuàng)建一個隊列記錄SQL執(zhí)行時間static readonly ConcurrentDictionary<DbCommand, DateTime> MStartTime = new ConcurrentDictionary<DbCommand, DateTime>();private ILogger<DBlogCommandInterceptor> _logger { get; set; }//通過構(gòu)造函數(shù)注入日志public DBlogCommandInterceptor(ILogger<DBlogCommandInterceptor> Logger){_logger = Logger;} }

創(chuàng)建2個私有的方法,一個記錄執(zhí)行開始時間,一個記錄SQL

      //記錄SQL開始執(zhí)行的時間 private void OnStart(DbCommand command){MStartTime.TryAdd(command, DateTime.Now);}//通過_logger輸出日志private void Log(DbCommand command){DateTime startTime;TimeSpan duration;//得到此command的開始時間MStartTime.TryRemove(command, out startTime);if (startTime != default(DateTime)){duration = DateTime.Now - startTime;}else{duration = TimeSpan.Zero;}var parameters = new StringBuilder();//循環(huán)獲取執(zhí)行語句的參數(shù)值foreach (DbParameter param in command.Parameters){parameters.AppendLine(param.ParameterName + " " + param.DbType + " = " + param.Value);}_logger.LogInformation("{starttime}開始執(zhí)行SQL語句:{sql},參數(shù):{canshu},執(zhí)行時間{readtime}",startTime.ToString(), command.CommandText, parameters.ToString(), duration.TotalSeconds);}

最后重載攔截器的方法:

public override InterceptionResult<int> NonQueryExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<int> result){OnStart(command);return base.NonQueryExecuting(command, eventData, result);}public override Task<InterceptionResult<int>> NonQueryExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default){OnStart(command);return base.NonQueryExecutingAsync(command, eventData, result, cancellationToken);}public override int NonQueryExecuted(DbCommand command, CommandExecutedEventData eventData, int result){Log(command);return base.NonQueryExecuted(command, eventData, result);}public override Task<int> NonQueryExecutedAsync(DbCommand command, CommandExecutedEventData eventData, int result, CancellationToken cancellationToken = default){Log(command);return base.NonQueryExecutedAsync(command, eventData, result, cancellationToken);}public override InterceptionResult<object> ScalarExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<object> result){OnStart(command);return base.ScalarExecuting(command, eventData, result);}public override Task<InterceptionResult<object>> ScalarExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<object> result, CancellationToken cancellationToken = default){OnStart(command);return base.ScalarExecutingAsync(command, eventData, result, cancellationToken);}public override object ScalarExecuted(DbCommand command, CommandExecutedEventData eventData, object result){Log(command);return base.ScalarExecuted(command, eventData, result);}public override Task<object> ScalarExecutedAsync(DbCommand command, CommandExecutedEventData eventData, object result, CancellationToken cancellationToken = default){Log(command);return base.ScalarExecutedAsync(command, eventData, result, cancellationToken);}public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result){OnStart(command);return base.ReaderExecuting(command, eventData, result);}public override Task<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result, CancellationToken cancellationToken = default){OnStart(command);return base.ReaderExecutingAsync(command, eventData, result, cancellationToken);}public override Task<DbDataReader> ReaderExecutedAsync(DbCommand command, CommandExecutedEventData eventData, DbDataReader result, CancellationToken cancellationToken = default){Log(command);return base.ReaderExecutedAsync(command, eventData, result, cancellationToken);}public override DbDataReader ReaderExecuted(DbCommand command, CommandExecutedEventData eventData, DbDataReader result){Log(command);return base.ReaderExecuted(command, eventData, result);}

這樣,我們就實現(xiàn)了通過攔截器實現(xiàn)SQL日志記錄~效果如下:

?

?調(diào)試SQL語句就方便了很多~

總結(jié)

以上是生活随笔為你收集整理的EF Core3.0+ 通过拦截器实现读写分离与SQL日志记录的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。