1. 程式人生 > >企業專案實戰 .Net Core + Vue/Angular 分庫分表日誌系統六 | 最終篇-通過AOP自動連線資料庫-完成日誌業務

企業專案實戰 .Net Core + Vue/Angular 分庫分表日誌系統六 | 最終篇-通過AOP自動連線資料庫-完成日誌業務

# 教程預覽 [01 | 前言](https://www.cnblogs.com/HDONG/p/13517146.html) [02 | 簡單的分庫分表設計](https://www.cnblogs.com/HDONG/p/13517207.html) [03 | 控制反轉搭配簡單業務](https://www.cnblogs.com/HDONG/p/13527308.html) [04 | 強化設計方案](https://www.cnblogs.com/HDONG/p/13539186.html) [05 | 完善業務自動建立資料庫](https://www.cnblogs.com/HDONG/p/13552014.html) [06 | 最終篇-通過AOP自動連線資料庫-完成日誌業務](https://www.cnblogs.com/HDONG/p/13575511.html) #前言 這周比較忙,這篇來的有點遲到,不過我們要講的東西是非常精彩的,通過之前的文章我們的設計已經完成,而且完成了 ProjectController 的業務操作,成功生成了分庫的日誌資料庫和表,那麼在操作日誌 Controller 的時候,我們如何來連線多個數據庫 和 多張表呢。 #理論講解 首先我們如果要動態連線資料庫那麼第一想到的就是中介軟體,AOP,那我們我們的資料庫連線儲存在哪裡呢 在第二節的時候將的 DefaultSqlSugarProviderStorage 連線提供程式儲存器 DataMap 中儲存著我們的連線,我們只要動態的往裡面加入 連線就可以了。 #正文 ###1.基本部分 首先我們在 EasyTools 資料夾新建 IocManager 類 ``` public class IocManager { public static IServiceCollection Services { get; private set; } public static IServiceProvider ServiceProvider { get; private set; } public static IConfiguration Configuration { get; private set; } static IocManager() { Services = new ServiceCollection(); } public static IServiceProvider Build() { ServiceProvider = Services.BuildServiceProvider(); return ServiceProvider; } public static void SetConfiguration(IConfiguration configuration) { Configuration = configuration; } public static void SetServiceProvider(IServiceProvider serviceProvider) { if (ServiceProvider == null) { return; } ServiceProvider = serviceProvider; } } ``` 對IocManager的引數進行初始化,方便呼叫Configuration 和呼叫 ServiceProvider ![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/20200827231730.png) ![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/20200827232014.png) ###2.增加AOP 之前說的我們不能手動去搞資料庫連線,那麼這裡我就藉助AOP來做 安裝依賴包 ``` Autofac Autofac.Extensions.DependencyInjection Autofac.Extras.DynamicProxy ``` 在 Program 中加入下面這行程式碼 這是 Autofac在Core 3.0之後的用法 ![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/20200827232415.png) ``` .UseServiceProviderFactory(new AutofacServiceProviderFactory()) ``` 在Startup 新建方法 ConfigureContainer Autofac會在啟動的時候預設呼叫,大家可能對我寫在 ConfigureContainer 方法中的感到好奇,那麼這是什麼呢, 和之前倉儲一樣,SqlSugar 和 其他ORM框架的動態連線資料庫 程式碼不一樣所以 我們先建立基類進行約束 然後各自ORM進行實現,因為這部分屬於業務層程式碼,所以我沒有 放到 EasyLogger.DbStorage 而是放在啟動程式中 ![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/20200827232542.png) ``` public void ConfigureContainer(ContainerBuilder builder) { builder.RegisterType().As().EnableClassInterceptors(); builder.RegisterType(); } ``` ![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/20200827233044.png) 至於 SqlSugarDynamicLinkAop 就是我們的動態連線資料庫的AOP方法,下面我們開始實現他們 ![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/20200827232921.png) 首先動態連線資料庫的關鍵依據 是查詢的時間,我們的資料庫分庫規則是一個月一個數據庫,一天一張表,那麼我們就先來定義一個規範的DTO ``` public class DynamicLinkInput: PagedInput { public DateTime TimeStart { get; set; } public DateTime TimeEnd { get; set; } } ``` 在 AOP 資料夾 新建 DynamicLinkAopBase 介面約束ORM的連線 ``` public abstract class DynamicLinkAopBase : IInterceptor { /// /// AOP的攔截方法 ///
/// public abstract void Intercept(IInvocation invocation); /// /// 獲取查詢所需的必要條件 /// /// /// public DynamicLinkInput GetTiemRange(IInvocation invocation) { var methodArguments = invocation.Arguments.FirstOrDefault();//獲取引數列表 var input = (DynamicLinkInput)methodArguments; return input; } public DynamicLinkAttribute GetDynamicLinkAttributeOrNull(MethodInfo methodInfo) { var attrs = methodInfo.GetCustomAttributes(true).OfType().ToArray(); if(attrs.Length > 0) { return attrs[0]; } attrs = methodInfo.DeclaringType.GetTypeInfo().GetCustomAttributes(true).OfType().ToArray(); if (attrs.Length > 0) { return attrs[0]; } return null; } } ``` 在 AOP 資料夾 新建 DynamicLinkAttribute 註解 ``` [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] public class DynamicLinkAttribute: Attribute { public bool IsDisabled { get; set; } } ``` 實現 SqlSugar 的 動態連線 SqlSugarDynamicLinkAop 那麼這個AOP幹了啥呢 #####1.我判斷這個類是否進行動態資料庫連線 #####2.我獲取到必要的開始結束時間,來獲取之前產生了多少個月份 #####3.把這些月份動態的加入到 連線提供程式儲存器。 ``` public class SqlSugarDynamicLinkAop : DynamicLinkAopBase { private readonly IServiceProvider _serviceProvider; public override void Intercept(IInvocation invocation) { MethodInfo method; try { method = invocation.MethodInvocationTarget; } catch (Exception ex) { method = invocation.GetConcreteMethod(); } var dynamicLinkAttr = GetDynamicLinkAttributeOrNull(method); if (dynamicLinkAttr == null || dynamicLinkAttr.IsDisabled) { invocation.Proceed();//直接執行被攔截方法 } else { var input = this.GetTiemRange(invocation); var dateList = TimeTools.GetMonthByList(input.TimeStart.ToString("yyyy-MM"), input.TimeEnd.ToString("yyyy-MM")); foreach (var item in dateList) { var DbName = $"{IocManager.Configuration["EasyLogger:DbName"]}-{item.ToString("yyyy-MM")}"; var dbPathName = Path.Combine(PathExtenstions.GetApplicationCurrentPath(), DbName + ".db"); IocManager.ServiceProvider.AddSqlSugarDatabaseProvider(new SqlSugarSetting() { Name = DbName, ConnectionString = @$"Data Source={dbPathName}", DatabaseType = DbType.Sqlite, LogExecuting = (sql, pars) => { Console.WriteLine($"sql:{sql}"); } }); } invocation.Proceed();//直接執行被攔截方法 } } } ``` 這裡用的 AddSqlSugarDatabaseProvider 之前沒有寫 其實如果看懂了,之前的說明,這裡怎麼寫大家都能寫出來,就是獲取到 連線提供程式儲存器 往裡面加入了一個連線。 ![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/20200827234703.png) ``` public static IServiceProvider AddSqlSugarDatabaseProvider(this IServiceProvider serviceProvider, ISqlSugarSetting dbSetting) { if (dbSetting == null) { throw new ArgumentNullException(nameof(dbSetting)); } var fSqlProviderStorage = serviceProvider.GetRequiredService(); fSqlProviderStorage.AddOrUpdate(dbSetting.Name, new SqlSugarProvider(dbSetting)); return serviceProvider; } ``` 那麼這個AOP 怎麼用呢,新建介面 IDynamicLinkBase 來提供 AOP呼叫 實現類是 SqlSugarDynamicLink (這裡直接用類也可以 我只是個人習慣) ![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/20200827233436.png) ![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/20200827235023.png) ####基本上到此為止,大家已經看明白路線了 1.我們呼叫約束的Dto 傳遞開始、結束時間 2.AOP攔截到我們條件,判斷方法是否需要動態注入連線 3.根據開始結束時間 把範圍內的資料庫都連線上,其中 我們做了一個最大開始時間 和 最大結束時間的判斷,防止資料庫沒有出現連線錯誤 #業務測驗邏輯 老規矩 新建 EasyLoggerRecordDto資料夾 儲存Dto ![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/20200827235533.png) ``` public class CreateOrUpdateEasyLoggerRecordInput { public EasyLoggerRecordEditDto EasyLoggerRecord { get; set; } } public class EasyLoggerRecordEditDto { public int? Id { get; set; } /// /// 專案Id ///
public int ProjectId { get; set; } /// /// 型別.自定義標籤 /// public string LogType { get; set; } /// /// 狀態-成功、失敗、警告等 /// public string LogState { get; set; } /// /// 標題 /// public string LogTitle { get; set; } /// /// 內容描述 ///
public string LogContent { get; set; } /// /// 在系統中產生的時間 /// public DateTime LogTime { get; set; } } public class EasyLoggerRecordInput : DynamicLinkInput { /// /// 專案Id /// public int? ProjectId { get; set; } /// /// 型別.自定義標籤 /// public string LogType { get; set; } /// /// 狀態-成功、失敗、警告等 /// public string LogState { get; set; } /// /// 標題 /// public string LogTitle { get; set; } } public class EasyLoggerRecordListDto { public int Id { get; set; } /// /// 專案Id /// public int ProjectId { get; set; } /// /// 型別.自定義標籤 /// public string LogType { get; set; } /// /// 狀態-成功、失敗、警告等 /// public string LogState { get; set; } /// /// 標題 /// public string LogTitle { get; set; } /// /// 內容描述 /// public string LogContent { get; set; } /// /// 在系統中產生的時間 /// public DateTime LogTime { get; set; } public EasyLoggerProjectEditDto EasyLoggerProject { get; set; } /// /// 建立時間 /// public DateTime CreateTime { get; set; } } ``` 新建 LoggerController 注入所需依賴 ![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/20200827235635.png) ###這裡我就先貼一張圖大家來看看整個流程 怎麼玩的思考一下 #####1.首先執行AOP 並且拿到注入了那些連線 #####2.從預設庫中獲取我們的專案資訊儲存到記憶體 #####3.我們通過 得到的注入連線,來進行日誌的查詢 #####4.我們關聯上每個日誌所屬的專案 返回結果 ![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/20200827235740.png) ###我們在來細看摺疊部分的邏輯 ![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/20200828000143.png) #####1.Item是一個庫 我們拿到這個庫所有的天數表 #####2.使用 ChangeProvider 來切換資料庫連線 連線到 Item這個時間點的資料庫 #####3.通過SqlSugar提供的方法進行 UnionAll #####4.查詢資料加入返回列表中 ``` [HttpPost("GetEasyLoggerAsync")] [DynamicLink] public async Task> GetEasyLoggerAsync(EasyLoggerRecordInput input) { // 獲取查詢的時間範圍 var dateList = _linkBase.DynamicLinkOrm(input).OrderByDescending(s => s).ToList(); var result = new PagedResultDto(); // 查詢初始資料庫資料 var projectList = _sqlRepository.GetCurrentSqlSugar().Queryable().ToList(); var DbName = IocManager.Configuration["EasyLogger:DbName"]; var entityList = new List(); // 為跨庫查詢定義的引數 int Sumtotal = 0; foreach (var item in dateList) { var dayList = TimeTools.GetDayDiff(item.AddDays(1 - DateTime.Now.Day).Date, item.AddDays(1 - DateTime.Now.Day).Date.AddMonths(1).AddSeconds(-1)); using (_sqlRepository.ChangeProvider($"{DbName}-" + item.ToString("yyyy-MM"))) { var sqlSugarClient = _sqlRepository.GetCurrentSqlSugar(); var queryables = new List>(); _sqlRepository.GetCurrentSqlSugar().Queryable(); foreach (var day in dayList) { queryables.Add(sqlSugarClient.Queryable().AS($"EasyLoggerRecord_{day}")); } var sqlSugarLogger = sqlSugarClient.UnionAll(queryables); var data = sqlSugarLogger .Where(s => s.CreateTime >= input.TimeStart) .Where(s => s.CreateTime <= input.TimeEnd) .WhereIF(!string.IsNullOrWhiteSpace(input.LogTitle), s => s.LogTitle == input.LogTitle) .WhereIF(!string.IsNullOrWhiteSpace(input.LogType), s => s.LogType == input.LogType) .WhereIF(input.ProjectId != null, s => s.ProjectId == input.ProjectId) .WhereIF(input.LogState != null, s => s.LogState == input.LogState) .OrderBy(s => s.CreateTime, OrderByType.Desc) .ToPageList(input.PageIndex, input.PageSize, ref Sumtotal); entityList.AddRange(data); } } result.Total = Sumtotal; result.List = _mapper.Map>(entityList); foreach (var item in result.List) { var project = projectList.Where(s => s.Id == item.ProjectId).FirstOrDefault(); item.EasyLoggerProject = _mapper.Map(project); } return result; } ``` ##測試 ####AOP拿到時間,進行動態新增 ![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/20200828000621.png) ####DataMap也有我們需要的連線 ![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/20200828000743.png) ####我們將8月31張表進行 UnionAll ![](https://git.imweb.io/hdong/ImageBed/raw/master/EasyLoggerImages/20200828000837.png) ####到此我們整個專案業務重點部分完成 #思考 該教程的核心 部分已經全部講解完畢,整套的架構設計也已經定下來了,如果你從頭開始整套的跟完我想就算你是中級開發,我想你也能從中學到一些設計思想。 這一節我是先把程式碼寫出來進行講解,而且思考部分很多,我希望該節能讓大家自己手寫,去會議我們整套架構一步一步如何設計出來的,而不是直接抄程式碼執行沒問題完事! #問題 SqlSugar 直接業務程式碼寫在控制器中,不能直接切換ORM 查詢如果多個月進行查詢,如何分頁資料 定時計劃進行資料庫的建立 其他系統應該如何接入該系統 #結尾 提出的問題請認真思考,如果只是看看那就過眼雲煙吧! 後端暫定完結撒花-前端坑慢慢填(主要前端沒啥技術點需要講,這個專案前端就是CRUD)! 後面針對技術點進行基礎 + 專案場景下的實戰應用 喜歡的老闆點關注不迷路!