Day 2 Abp框架下,MySQL数据迁移时,添加表和字段注释
后端采用Abp框架,当前最新版本是7.4.0。
数据库使用MySQL,在执行数据库迁移时,写在Domain层的Entity类上的注释通通都没有,这样查看数据库字段的含义时,就需要对照代码来看,有些不方便。今天专门来解决这个问题。
还是一顿搜索,发现了两个方案:
abp 框架拓展mysql 迁移:增加数据库表和列备注
EFcore+MySql 数据迁移的时候,怎么给表结构加注释?
上述两篇文章,
第一篇重载 MySqlMigrationsSqlGenerator 来实现加注释,但是字段注释是通过Description属性来获取的,这样字段上就要注释和Description重复写两遍。
第二篇直接通过工具,读取xml文档,生成 HasComment相关代码,每次都需要手动修改DbContext代码。
都不是特别完美,所以结合一下看看。具体方案还是重载MySqlMigrationsSqlGenerator,但是通过读取xml来获取信息。
首先是SqlGenerator:
/// <summary>
/// 拓展迁移操作:增加数据表和列备注
/// </summary>
public class MyMigrationsSqlGenerator : MySqlMigrationsSqlGenerator
{public MyMigrationsSqlGenerator(MigrationsSqlGeneratorDependencies dependencies,IMigrationsAnnotationProvider migrationsAnnotations,ICommandBatchPreparer commandBatchPreparer,IMySqlOptions mySqlOptions): base(dependencies, commandBatchPreparer, mySqlOptions){}protected override void Generate(MigrationOperation operation, IModel model, MigrationCommandListBuilder builder){base.Generate(operation, model, builder);if (operation is CreateTableOperation || operation is AlterTableOperation)CreateTableComment(operation, model, builder);if (operation is AddColumnOperation || operation is AlterColumnOperation)CreateColumnComment(operation, model, builder);}/// <summary>/// 创建表注释/// </summary>/// <param name="operation"></param>/// <param name="builder"></param>private void CreateTableComment(MigrationOperation operation, IModel model, MigrationCommandListBuilder builder){string tableName = string.Empty;string description = string.Empty;if (operation is AlterTableOperation){var t = operation as AlterColumnOperation;tableName = (operation as AlterTableOperation).Name;}if (operation is CreateTableOperation){var t = operation as CreateTableOperation;var addColumnsOperation = t.Columns;tableName = t.Name;foreach (var item in addColumnsOperation){CreateColumnComment(item, model, builder);}}//description = DbDescriptionHelper.GetDescription(tableName.Replace(jingdianConsts.DbTablePrefix, ""));description = GetDescription(tableName, null);if (tableName.IsNullOrWhiteSpace())throw new Exception("表名为空引起添加表注释异常.");var sqlHelper = Dependencies.SqlGenerationHelper;builder.Append("ALTER TABLE ").Append(sqlHelper.DelimitIdentifier(tableName)).Append(" COMMENT ").Append("'").Append(description).Append("'").AppendLine(sqlHelper.StatementTerminator).EndCommand();}/// <summary>/// 创建列注释/// </summary>/// <param name="operation"></param>/// <param name="builder"></param>private void CreateColumnComment(MigrationOperation operation, IModel model, MigrationCommandListBuilder builder){//alter table a1log modify column UUID VARCHAR(26) comment '修改后的字段注释';string tableName = string.Empty;string columnName = string.Empty;string columnType = string.Empty;string description = string.Empty;if (operation is AlterColumnOperation){var t = (operation as AlterColumnOperation);columnType = t.ColumnType;}if (operation is AddColumnOperation){var t = (operation as AddColumnOperation);columnType = t.ColumnType;description = GetDescription(tableName, columnName);}if (columnName.IsNullOrWhiteSpace() || tableName.IsNullOrWhiteSpace() || columnType.IsNullOrWhiteSpace())throw new Exception("列名为空或表名为空或列类型为空引起添加列注释异常." + columnName + "/" + tableName + "/" + columnType);var sqlHelper = Dependencies.SqlGenerationHelper;builder.Append("ALTER TABLE ").Append(sqlHelper.DelimitIdentifier(tableName)).Append(" MODIFY COLUMN ").Append(columnName).Append(" ").Append(columnType).Append(" COMMENT ").Append("'").Append(description).Append("'").AppendLine(sqlHelper.StatementTerminator).EndCommand();}private string GetDescription(string tableName, string? columnName){var type = TableCommentRegister.Types[tableName];if (type == null){return string.Empty;}string xmlPath = type.Module.Name.Replace("dll","xml");XmlDocument xml = new XmlDocument();xml.Load(xmlPath);var classNode = xml.SelectSingleNode(($"//member[@name='T:{type.FullName}']"));if (classNode == null){return string.Empty;}if (columnName == null){return classNode.InnerText.Trim();}else{var propertyNode = xml.SelectSingleNode(($"//member[@name='P:{type.FullName}.{columnName}']"));if (propertyNode == null){return string.Empty;}return propertyNode.InnerText.Trim();}}
}
这里面有一个点,生成Sql时,只知道table name和column name,一般情况下列名就是属性名,可以不考虑,但是表名和类名可能会有差异,比如前后缀之类的。参考1中,就是直接做替换,我考虑还是做了一个静态字典对象,把表名和类名做了一个映射,具体如下:
/// <summary>
/// 数据表注册器
/// </summary>
public static class TableCommentRegister
{public static Dictionary<string, Type> Types { get; set; } = new Dictionary<string, Type>();/// <summary>/// 配置实体对应的数据表,同时注册类型,用于后续生成备注/// </summary>/// <typeparam name="T">实体类型</typeparam>/// <param name="builder"></param>/// <param name="tableName">数据表名</param>public static void ToTableWithComment<T>(this EntityTypeBuilder<T> builder, string tableName) where T : class{builder.ToTable(tableName);Types.TryAdd(tableName, typeof(T));}
}
然后在DbContext类中,针对表的处理代码如下:
builder.Entity<Company>(b =>{string tableName = Consts.DbTablePrefix + "Company";b.ToTableWithComment(tableName);b.ConfigureByConvention(); //auto configure for the base class props});
就是把之前的 ToTable 改成 ToTableWithComment 就可以了。
最后,需要修改DbSchemaMigrator类,把SqlGenerator注册进去。这里我就简单粗暴的复制了一下DbContextFactory类。因为DbContextFactory代码注释则表明了其只是用于EF Core console commands,在Abp的DbMigrator程序中不起作用。
DbSchemaMigrator类中,Abp 脚手架代码应该是这样的:
public async Task MigrateAsync()
{/* We intentionally resolving the XiuYuanDbContext* from IServiceProvider (instead of directly injecting it)* to properly get the connection string of the current tenant in the* current scope.*/await _serviceProvider.GetRequiredService<XiuYuanDbContext>().Database.MigrateAsync();
}
修改如下:
public async Task MigrateAsync()
{ await CreateDbContext() .Database.MigrateAsync();
}public xxxDbContext CreateDbContext()
{xxxEfCoreEntityExtensionMappings.Configure();var configuration = BuildConfiguration();var connection = configuration.GetConnectionString(xxxConsts.DbSchema);var builder = new DbContextOptionsBuilder<xxxDbContext>().UseMySql(connection, ServerVersion.AutoDetect(connection), o => o.SchemaBehavior(MySqlSchemaBehavior.Ignore))// 注意这里的ReplaceService.ReplaceService<IMigrationsSqlGenerator, MyMigrationsSqlGenerator>();return new xxxDbContext(builder.Options);
}private static IConfigurationRoot BuildConfiguration()
{var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json", optional: false);return builder.Build();
}
至此,所有基础性工作都完成了,后面再添加领域模型时,记得把ToTable改成ToTableWithComment即可。
相关文章:
Day 2 Abp框架下,MySQL数据迁移时,添加表和字段注释
后端采用Abp框架,当前最新版本是7.4.0。 数据库使用MySQL,在执行数据库迁移时,写在Domain层的Entity类上的注释通通都没有,这样查看数据库字段的含义时,就需要对照代码来看,有些不方便。今天专门来解决这个…...
传智教育研究院重磅发布Java学科新研发《智慧养老》项目
在招聘Java开发人才的过程中,企业往往对候选人的项目经验有着严格的要求,项目经验成为顺利就业的重要敲门砖之一。而在数字化技术的学习中,如何让学员通过项目课程有效地积累实战开发经验,就成了数字化技术职业教育的一个重大难点…...
Fiddler抓包VSCode和探索
前言: 最近在使用 VSCode 调试 web 程序时,遇到一些问题,当时不知道如何是好。所以决定抓看来看一看,然后一顿操作猛如虎,成功安装了抓包软件 – Fiddler Classic。我并没有使用 Postman 这种重量级的 HTTP 测试软件&a…...
Pytorch指定数据加载器使用子进程
torch.utils.data.DataLoader(train_dataset, batch_sizebatch_size, shuffleTrue,num_workers4, pin_memoryTrue) num_workers 参数是 DataLoader 类的一个参数,它指定了数据加载器使用的子进程数量。通过增加 num_workers 的数量,可以并行地读取和预处…...
【科普】干货!带你从0了解移动机器人(六) (底盘结构类型)
牵引式移动机器人(AGV/AMR),通常由一个牵引车和一个或多个被牵引的车辆组成。牵引车是机器人的核心部分,它具有自主导航和定位功能,可以根据预先设定的路径或地标进行导航,并通过传感器和视觉系统感知周围环…...
爆肝整理,Pytest+Allure+Jenkins自动化测试集成实战(图文详细步骤)
目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 1、简介 pytesta…...
微信批量添加好友,让你的人脉迅速增长
在这个数字化时代,微信作为中国最流行的社交平台之一,已经成为了人们生活中不可或缺的一部分。它的广泛使用为我们提供了无限的社交可能性。你是否曾为了扩大人脉圈子而犯愁?今天,我将向你揭示一个高效添加微信好友的秘密武器&…...
3D模型怎么贴法线贴图?
1、法线贴图的原理? 法线贴图(normal mapping)是一种计算机图形技术,用于在低多边形模型上模拟高多边形模型的细节效果。它通过在纹理坐标上存储和应用法线向量的信息来实现。 法线贴图的原理基于光照模型。在渲染过程中&#x…...
QT中文乱码解决方案与乱码的原因
相信大家应该都遇到过中文乱码的问题,有时候改一改中文就不乱码了,但是有时候用同样的方式还是乱码,那么这个乱码到底是什么原因,又该如何彻底解决呢? 总结 先总结一下: Qt5中,将QString()的构…...
sam9x60 boot
...
3D模型格式转换工具HOOPS Exchange:支持国际标准STEP格式!
HOOPS Exchange SDK是一组C软件库,使开发团队能够快速将可靠的2D和3D CAD导入和导出添加到其应用程序中,访问广泛的数据,包括边界表示 (B-REP)、产品制造信息 (PMI)、模型树、视图、持久 ID、样式、构造几何、可视化等,无需依赖任…...
java--死循环与循环嵌套
1.死循环 可以一直执行下去的一种循环,如果没有干预不会停下来的 2.死循环的写法 3.循环嵌套 循环中又包含循环 4.循环嵌套的特点 外部循环每循环一次,内部循环会全部执行完一轮...
基于机器视觉的图像拼接算法 计算机竞赛
前言 图像拼接在实际的应用场景很广,比如无人机航拍,遥感图像等等,图像拼接是进一步做图像理解基础步骤,拼接效果的好坏直接影响接下来的工作,所以一个好的图像拼接算法非常重要。 再举一个身边的例子吧,…...
基于arduino uno + L298 的直流电机驱动proteus仿真设计
一、L298简介: L298是一个集成的单片电路,采用15个导线多瓦和PowerSO20封装。它是一个高电压、高电流双全桥驱动器,旨在接受标准TTL逻辑电平和驱动感应负载,如继电器、螺线管、直流和加速电机。提供两个使输入来使独立于输入信号的…...
cola架构:有限状态机(FSM)源码分析
目录 0. cola状态机简述 1.cola状态机使用实例 2.cola状态机源码解析 2.1 语义模型源码 2.1.1 Condition和Action接口 2.1.2 State 2.1.3 Transition接口 2.1.4 StateMachine接口 2.2 Builder模式 2.2.1 StateMachine Builder模式 2.2.2 ExternalTransitionBuilder-…...
通信仿真软件SystemView安装教程(超详细)
介绍 system view是一种电子仿真工具。它是一个信号级的系统仿真软件,主要用于电路与通信系统的设计和仿真,是一个强有力的动态系统分析工具,能满足从数字信号处理,滤波器设计,直到复杂的通信系统等不同层次的设计&am…...
Go学习第八章——面向“对象”编程(入门——结构体与方法)
Go面向“对象”编程(入门——结构体与方法) 1 结构体1.1 快速入门1.2 内存解析1.3 创建结构体四种方法1.4 注意事项和使用细节 2 方法2.1 方法的声明和调用2.2 快速入门案例2.3 调用机制和传参原理2.4 注意事项和细节2.5 方法和函数区别 3 工厂模式 Gola…...
「滚雪球学Java」:方法函数(章节汇总)
🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎大家关注&&收藏!持续更新中,up!up!up!…...
数据分析必备原理思路(二)
文章目录 三、主流的数据分析方法与框架使用1. 五个数据分析领域关键的理论基础(1)大数定律(2)罗卡定律(3)幸存者偏差(4)辛普森悖论(5)帕累托最优(…...
分布式ID系统设计(1)
分布式ID系统设计(1) 在分布式服务中,需要对data和message进行唯一标识。 比如订单、支付等。然后在数据库分库分表之后也需要一个唯一id来表示。 基于DB的自增就肯定不能满足了。这个时候能够生成一个Global的唯一ID的服务就很有必要我们姑且把它叫做id-server 。…...
使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
tomcat入门
1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效,稳定,易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...
嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...
绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化
iOS 应用的发布流程一直是开发链路中最“苹果味”的环节:强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说,这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发(例如 Flutter、React Na…...
2.3 物理层设备
在这个视频中,我们要学习工作在物理层的两种网络设备,分别是中继器和集线器。首先来看中继器。在计算机网络中两个节点之间,需要通过物理传输媒体或者说物理传输介质进行连接。像同轴电缆、双绞线就是典型的传输介质,假设A节点要给…...
