教程预览

01 | 前言

02 | 简单的分库分表设计

03 | 控制反转搭配简单业务

04 | 强化设计方案

05 | 完善业务自动创建数据库

06 | 最终篇-通过AOP自动连接数据库-完成日志业务

前言

这周比较忙,这篇来的有点迟到,不过我们要讲的东西是非常精彩的,通过之前的文章我们的设计已经完成,而且完成了 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



2.增加AOP 之前说的我们不能手动去搞数据库连接,那么这里我就借助AOP来做

安装依赖包

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干了啥呢

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 之前没有写 其实如果看懂了,之前的说明,这里怎么写大家都能写出来,就是获取到 连接提供程序存储器 往里面加入了一个连接。

        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 注入所需依赖

这里我就先贴一张图大家来看看整个流程 怎么玩的思考一下

1.首先执行AOP 并且拿到注入了那些连接
2.从默认库中获取我们的项目信息存储到内存
3.我们通过 得到的注入连接,来进行日志的查询
4.我们关联上每个日志所属的项目 返回结果

我们在来细看折叠部分的逻辑

1.Item是一个库 我们拿到这个库所有的天数表
2.使用 ChangeProvider 来切换数据库连接 连接到 Item这个时间点的数据库
3.通过SqlSugar提供的方法进行 UnionAll
4.查询数据加入返回列表中

[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;
}

测试

AOP拿到时间,进行动态添加

DataMap也有我们需要的连接

我们将8月31张表进行 UnionAll

到此我们整个项目业务重点部分完成

思考

该教程的核心 部分已经全部讲解完毕,整套的架构设计也已经定下来了,如果你从头开始整套的跟完我想就算你是中级开发,我想你也能从中学到一些设计思想。

这一节我是先把代码写出来进行讲解,而且思考部分很多,我希望该节能让大家自己手写,去会议我们整套架构一步一步如何设计出来的,而不是直接抄代码运行没问题完事!

问题

SqlSugar 直接业务代码写在控制器中,不能直接切换ORM

查询如果多个月进行查询,如何分页数据

定时计划进行数据库的创建

其他系统应该如何接入该系统

结尾

提出的问题请认真思考,如果只是看看那就过眼云烟吧!

后端暂定完结撒花-前端坑慢慢填(主要前端没啥技术点需要讲,这个项目前端就是CRUD)!

后面针对技术点进行基础 + 项目场景下的实战应用 喜欢的老板点关注不迷路!

#企业项目实战 .Net Core + Vue/Angular 分库分表日志系统六 | 最终篇-通过AOP自动连接数据库-完成日志业务的更多相关文章

  1. 企业项目实战 .Net Core + Vue/Angular 分库分表日志系统一 | 前言

    教程预览 01 | 前言 02 | 简单的分库分表设计 03 | 控制反转搭配简单业务 04 | 强化设计方案 05 | 完善业务自动创建数据库 06 | 最终篇-通过AOP自动连接数据库-完成日志业 ...

  2. 企业项目实战 .Net Core + Vue/Angular 分库分表日志系统二 | 简单的分库分表设计

    教程预览 01 | 前言 02 | 简单的分库分表设计 03 | 控制反转搭配简单业务 04 | 强化设计方案 05 | 完善业务自动创建数据库 06 | 最终篇-通过AOP自动连接数据库-完成日志业 ...

  3. 企业项目实战 .Net Core + Vue/Angular 分库分表日志系统三 | 控制反转搭配简单业务

    教程预览 01 | 前言 02 | 简单的分库分表设计 03 | 控制反转搭配简单业务 04 | 强化设计方案 05 | 完善业务自动创建数据库 06 | 最终篇-通过AOP自动连接数据库-完成日志业 ...

  4. 企业项目实战 .Net Core + Vue/Angular 分库分表日志系统 | 前言

    介绍 大家好我是初久,一名从业4年的.Net开发攻城狮,从今天开始我会和大家一起对企业开发中常用的技术进行分享,一方面督促自己学习,一方面也希望大家可以给我指点出更好的方案,我们一起进步. 项目背景 ...

  5. 企业项目实战 .Net Core + Vue/Angular 分库分表日志系统 | 控制反转搭配简单业务

    教程预览 01 | 前言 02 | 简单的分库分表设计 03 | 控制反转搭配简单业务 说明 我们上一节已经成功通过 连接提供程序存储库,获取到了 连接提供程序,但是连接提供程序和数据库连接依赖太深, ...

  6. 企业项目实战 .Net Core + Vue/Angular 分库分表日志系统五 | 完善业务自动创建数据库

    教程预览 01 | 前言 02 | 简单的分库分表设计 03 | 控制反转搭配简单业务 04 | 强化设计方案 05 | 完善业务自动创建数据库 说明 这节来把基础的业务部分完善一下. 因为 IQue ...

  7. 企业项目实战 .Net Core + Vue/Angular 分库分表日志系统四 | 强化设计方案

    教程预览 01 | 前言 02 | 简单的分库分表设计 03 | 控制反转搭配简单业务 04 | 强化设计方案 强化 先来记录一下我们现在的样子,一会好做个对比 1.在EasyLogger.DbSto ...

  8. 企业项目实战 .Net Core + Vue/Angular 分库分表日志系统 | 简单的分库分表设计

    前言 项目涉及到了一些设计模式,如果你看的不是很明白,没有关系坚持下来,写完之后去思考去品,你就会有一种突拨开云雾的感觉,所以请不要在半途感觉自己看不懂选择放弃,如果我哪里写的详细,或者需要修正请联系 ...

  9. mysql、oracle分库分表方案之sharding-jdbc使用(非demo示例)

    选择开源核心组件的一个非常重要的考虑通常是社区活跃性,一旦项目团队无法进行自己后续维护和扩展的情况下更是如此. 至于为什么选择sharding-jdbc而不是Mycat,可以参考知乎讨论帖子https ...

随机推荐

  1. PHP chop() 函数

    实例 移除字符串右侧的字符: <?php$str = "Hello World!";高佣联盟 www.cgewang.comecho $str . "<br& ...

  2. 解读生成对抗网络(GAN) 之U-GAN-IT

    Unsupervised Generative Attentionnal Networks with Adapter Layer-In(U-GAN-IT) 从字面我们可以理解为无监督生成对抗网络和适配 ...

  3. 一招教你如何在Python中使用Torchmoji将文本转换为表情符号

    很难找到关于如何使用Python使用DeepMoji的教程.我已经尝试了几次,后来又出现了几次错误,于是决定使用替代版本:torchMoji. TorchMoji是DeepMoji的pyTorch实现 ...

  4. 太鸡冻了!我用 Python 偷偷查到暗恋女生的名字

    1 目 标 场 景 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人,却不知道如何去学习更加高深的知识.那么针对这三类人 ...

  5. 5 年 Python 的我,总结了这 90 条写 Python 程序的建议

    自己写 Python 也有四五年了,一直是用自己的“强迫症”在维持自己代码的质量.都有去看Google的Python代码规范,对这几年的工作经验,做个简单的笔记,如果你也在学pythpn,准备要学习p ...

  6. Python 创建用户界面之 PyQt5 的使用

    之前给大伙介绍了下 tkinter,有朋友希望小帅b对其它的 Python  GUI 框架也说道说道,那么今天就来说说 PyQt5 如何创建用户界面.   很多人学习python,不知道从何学起.很多 ...

  7. Flink中的window、watermark和ProcessFunction

    一.Flink中的window 1,window简述  window 是一种切割无限数据为有限块进行处理的手段.Window 是无限数据流处理的核心,Window 将一个无限的 stream 拆分成有 ...

  8. Javascript注意点

    Javascript注意点 在img标签中的src如果为相对路径, 但是在js获取的时候会转为全路径 候选框中, 在执行onclick之前, 会由于html的特征先设置checked属性 为a标签添加 ...

  9. three.js 着色器材质之变量(三)

    这篇郭先生在练习一下着色器变量,在度娘上面或者官网上经常看到类似水波一样的效果,这篇就试着做一个这样的效果,顺便巩固一下顶点着色器和片元着色器,毕竟多多练习才能更好地掌握.效果如下图,在线案例请点击博 ...

  10. Spring quartz中取得ServletContext

    在开发javaWeb定时任务的时候,有些处理要取得应用的相对路径,这就需要用到ServletContext取得到这个路径 解决思路是在web应用启动时,把ServletContext提前注入到Sche ...