01 | 模塊化方案一html
01 | 前言git
02 | 簡單的分庫分表設計github
04 | 強化設計方案數據庫
06 | 最終篇-經過AOP自動鏈接數據庫-完成日誌業務架構
這周比較忙,這篇來的有點遲到,不過咱們要講的東西是很是精彩的,經過以前的文章咱們的設計已經完成,並且完成了 ProjectController 的業務操做,成功生成了分庫的日誌數據庫和表,那麼在操做日誌 Controller 的時候,咱們如何來鏈接多個數據庫 和 多張表呢。app
首先咱們若是要動態鏈接數據庫那麼第一想到的就是中間件,AOP,那咱們咱們的數據庫鏈接存儲在哪裏呢 在第二節的時候將的 DefaultSqlSugarProviderStorage 鏈接提供程序存儲器 DataMap 中存儲着咱們的鏈接,咱們只要動態的往裏面加入 鏈接就能夠了。框架
首先咱們在 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
安裝依賴包
Autofac Autofac.Extensions.DependencyInjection Autofac.Extras.DynamicProxy
在 Program 中加入下面這行代碼 這是 Autofac在Core 3.0以後的用法
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
在Startup 新建方法 ConfigureContainer Autofac會在啓動的時候默認調用,你們可能對我寫在 ConfigureContainer 方法中的感到好奇,那麼這是什麼呢,
和以前倉儲同樣,SqlSugar 和 其餘ORM框架的動態鏈接數據庫 代碼不同因此 咱們先建立基類進行約束 而後各自ORM進行實現,由於這部分屬於業務層代碼,因此我沒有 放到 EasyLogger.DbStorage 而是放在啓動程序中
public void ConfigureContainer(ContainerBuilder builder) { builder.RegisterType<SqlSugarDynamicLink>().As<IDynamicLinkBase>().EnableClassInterceptors(); builder.RegisterType<SqlSugarDynamicLinkAop>(); }
至於 SqlSugarDynamicLinkAop 就是咱們的動態鏈接數據庫的AOP方法,下面咱們開始實現他們
首先動態鏈接數據庫的關鍵依據 是查詢的時間,咱們的數據庫分庫規則是一個月一個數據庫,一天一張表,那麼咱們就先來定義一個規範的DTO
public class DynamicLinkInput: PagedInput { public DateTime TimeStart { get; set; } public DateTime TimeEnd { get; set; } }
在 AOP 文件夾 新建 DynamicLinkAopBase 接口約束ORM的鏈接
public abstract class DynamicLinkAopBase : IInterceptor { /// <summary> /// AOP的攔截方法 /// </summary> /// <param name="invocation"></param> public abstract void Intercept(IInvocation invocation); /// <summary> /// 獲取查詢所需的必要條件 /// </summary> /// <param name="invocation"></param> /// <returns></returns> 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<DynamicLinkAttribute>().ToArray(); if(attrs.Length > 0) { return attrs[0]; } attrs = methodInfo.DeclaringType.GetTypeInfo().GetCustomAttributes(true).OfType<DynamicLinkAttribute>().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幹了啥呢
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 以前沒有寫 其實若是看懂了,以前的說明,這裏怎麼寫你們都能寫出來,就是獲取到 鏈接提供程序存儲器 往裏面加入了一個鏈接。
public static IServiceProvider AddSqlSugarDatabaseProvider(this IServiceProvider serviceProvider, ISqlSugarSetting dbSetting) { if (dbSetting == null) { throw new ArgumentNullException(nameof(dbSetting)); } var fSqlProviderStorage = serviceProvider.GetRequiredService<ISqlSugarProviderStorage>(); fSqlProviderStorage.AddOrUpdate(dbSetting.Name, new SqlSugarProvider(dbSetting)); return serviceProvider; }
那麼這個AOP 怎麼用呢,新建接口 IDynamicLinkBase 來提供 AOP調用 實現類是
SqlSugarDynamicLink (這裏直接用類也能夠 我只是我的習慣)
1.咱們調用約束的Dto 傳遞開始、結束時間
2.AOP攔截到咱們條件,判斷方法是否須要動態注入鏈接
3.根據開始結束時間 把範圍內的數據庫都鏈接上,其中 咱們作了一個最大開始時間 和 最大結束時間的判斷,防止數據庫沒有出現鏈接錯誤
老規矩 新建 EasyLoggerRecordDto文件夾 存儲Dto
public class CreateOrUpdateEasyLoggerRecordInput { public EasyLoggerRecordEditDto EasyLoggerRecord { get; set; } } public class EasyLoggerRecordEditDto { public int? Id { get; set; } /// <summary> /// 項目Id /// </summary> public int ProjectId { get; set; } /// <summary> /// 類型.自定義標籤 /// </summary> public string LogType { get; set; } /// <summary> /// 狀態-成功、失敗、警告等 /// </summary> public string LogState { get; set; } /// <summary> /// 標題 /// </summary> public string LogTitle { get; set; } /// <summary> /// 內容描述 /// </summary> public string LogContent { get; set; } /// <summary> /// 在系統中產生的時間 /// </summary> public DateTime LogTime { get; set; } } public class EasyLoggerRecordInput : DynamicLinkInput { /// <summary> /// 項目Id /// </summary> public int? ProjectId { get; set; } /// <summary> /// 類型.自定義標籤 /// </summary> public string LogType { get; set; } /// <summary> /// 狀態-成功、失敗、警告等 /// </summary> public string LogState { get; set; } /// <summary> /// 標題 /// </summary> public string LogTitle { get; set; } } public class EasyLoggerRecordListDto { public int Id { get; set; } /// <summary> /// 項目Id /// </summary> public int ProjectId { get; set; } /// <summary> /// 類型.自定義標籤 /// </summary> public string LogType { get; set; } /// <summary> /// 狀態-成功、失敗、警告等 /// </summary> public string LogState { get; set; } /// <summary> /// 標題 /// </summary> public string LogTitle { get; set; } /// <summary> /// 內容描述 /// </summary> public string LogContent { get; set; } /// <summary> /// 在系統中產生的時間 /// </summary> public DateTime LogTime { get; set; } public EasyLoggerProjectEditDto EasyLoggerProject { get; set; } /// <summary> /// 建立時間 /// </summary> public DateTime CreateTime { get; set; } }
新建 LoggerController 注入所需依賴
[HttpPost("GetEasyLoggerAsync")] [DynamicLink] public async Task<PagedResultDto<EasyLoggerRecordListDto>> GetEasyLoggerAsync(EasyLoggerRecordInput input) { // 獲取查詢的時間範圍 var dateList = _linkBase.DynamicLinkOrm(input).OrderByDescending(s => s).ToList(); var result = new PagedResultDto<EasyLoggerRecordListDto>(); // 查詢初始數據庫數據 var projectList = _sqlRepository.GetCurrentSqlSugar().Queryable<EasyLoggerProject>().ToList(); var DbName = IocManager.Configuration["EasyLogger:DbName"]; var entityList = new List<EasyLoggerRecord>(); // 爲跨庫查詢定義的參數 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<ISugarQueryable<EasyLoggerRecord>>(); _sqlRepository.GetCurrentSqlSugar().Queryable<EasyLoggerRecord>(); foreach (var day in dayList) { queryables.Add(sqlSugarClient.Queryable<EasyLoggerRecord>().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<List<EasyLoggerRecordListDto>>(entityList); foreach (var item in result.List) { var project = projectList.Where(s => s.Id == item.ProjectId).FirstOrDefault(); item.EasyLoggerProject = _mapper.Map<EasyLoggerProjectEditDto>(project); } return result; }
該教程的核心 部分已經所有講解完畢,整套的架構設計也已經定下來了,若是你從頭開始整套的跟完我想就算你是中級開發,我想你也能從中學到一些設計思想。
這一節我是先把代碼寫出來進行講解,並且思考部分不少,我但願該節能讓你們本身手寫,去會議咱們整套架構一步一步如何設計出來的,而不是直接抄代碼運行沒問題完事!
SqlSugar 直接業務代碼寫在控制器中,不能直接切換ORM
查詢若是多個月進行查詢,如何分頁數據
定時計劃進行數據庫的建立
其餘系統應該如何接入該系統
提出的問題請認真思考,若是隻是看看那就過眼雲煙吧! 後端暫定完結撒花-前端坑慢慢填(主要前端沒啥技術點須要講,這個項目前端就是CRUD)! 後面針對技術點進行基礎 + 項目場景下的實戰應用 喜歡的老闆點關注不迷路!