教程预览

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. Django学习路11_向数据库中添加 和 获取指定条件数据

    在 views.py 中添加函数 向数据库中添加数据 def add_persons(request): for i in range(15): person = Person() flag = ra ...

  2. PHP date_sun_info() 函数

    ------------恢复内容开始------------ 实例 返回有关 2013 年 1 月 1 日,纬度 31.7667,经度 35.2333 的日出/日落和黄昏开始/黄昏结束的信息: < ...

  3. 笨办法学python3代码练习ex23.py 字符串字节串字符编码

    首先简单说一下字符编码的问题.平常遇到比较多的就是ASCII码(全称:美国信息交换标准码).ASCII码使用一个字节(8位)来表示一些常见的数字.英文字母以及一些控制字符.英语用128个符号编码就够了 ...

  4. python2.3嵌套if结构:

    #案例:存款100万的请款下,买宝马:老爸资助大于50万买宝马740:大于30万买宝马520:小于20万宝马320.存款大于50万小于100万买丰田:大于20万小于50万买二手车:小于20万自行车! ...

  5. 求求你们不要再用 RSA 私钥加密公钥解密了,这非常不安全!

    最近经常在网上看到有人说巨硬的 CNG(Cryptography Next Generation 即下一代加密技术) 只提供 RSA 公钥加密私钥解密,没有提供 RSA 私钥加密公钥解密,他们要自己封 ...

  6. Python监控你的女朋友/男朋友每天都在看哪些网站

    需求: 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人,却不知道如何去学习更加高深的知识.那么针对这三类人,我给大家提 ...

  7. 内网 Maven 编译

    内网 Maven 编译 有个特殊的需求,在不联网的情况下编译 Java 项目. 想到两种方案: 搭建 Nexus 私有镜像仓库. 直接把依赖 jar包 放在编译机的 maven 本地库中. 步骤简述 ...

  8. 在Linux系统中安装Tomcat详细教程

    首先在官网下载jdk和Tomcat的压缩包 这里下载jdk-8u241-linux-x64 .tar.gz 和apache-tomcat-8.5.50.tar.gz 然后解压jdk压缩包 tar –z ...

  9. Loss 总结:IoU loss总结

    object detection 损失:更加接近人眼的损失 what is IoU 如果两个框没有相交,根据定义,IoU=0,不能反映两者的距离大小(重合度).同时因为loss=0,没有梯度回传,无法 ...

  10. [机器学习] keras:MNIST手写数字体识别(DeepLearning 的 HelloWord程序)

    深度学习界的Hello Word程序:MNIST手写数字体识别 learn from(仍然是李宏毅老师<机器学习>课程):http://speech.ee.ntu.edu.tw/~tlka ...