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 。…...
利用快马ai快速生成c语言语法学习原型,直观掌握编程基础
今天想和大家分享一个特别实用的C语言学习小技巧。作为一个编程新手,我最近发现用InsCode(快马)平台可以快速搭建C语言学习原型,把抽象的概念变成看得见、能运行的代码,学习效果特别好。 为什么要用原型学习法 刚开始学C语言时,最…...
FireRed-OCR保姆级教程:一键部署,精准提取表格公式转Markdown
FireRed-OCR保姆级教程:一键部署,精准提取表格公式转Markdown 1. 引言:为什么选择FireRed-OCR? 在日常工作和学习中,我们经常遇到需要从PDF、图片等文档中提取表格、公式等内容的情况。传统OCR工具往往难以准确识别复…...
FPGA实战:8点FFT运算的Verilog实现与误差优化技巧
FPGA实战:8点FFT运算的Verilog实现与误差优化技巧 在数字信号处理领域,快速傅里叶变换(FFT)算法是频谱分析的核心工具。对于FPGA开发者而言,掌握FFT的硬件实现不仅能提升系统性能,更能深入理解算法与硬件的…...
Agent Skill 从使用到原理,一次讲清
目录前言1. 本期内容概览2. Agent Skill 是什么3. Agent Skill 的基本用法4. 高级用法(Reference)5. 高级用法(Script)6. 渐进式披露机制7. Agent Skill vs MCP结语参考前言 学习 UP 主 马克的技术工作坊 的 Agent Skill 从使用到…...
OpenClaw跨平台实战:Windows到Mac的Qwen3-32B配置迁移
OpenClaw跨平台实战:Windows到Mac的Qwen3-32B配置迁移 1. 为什么需要跨平台配置迁移? 去年冬天,我在Windows工作站上搭建了一套基于Qwen3-32B的OpenClaw自动化系统,用于处理日常的文档整理和数据分析任务。当公司配发新款MacBoo…...
三步解锁QQ空间历史说说备份:数据留存与管理实用指南
三步解锁QQ空间历史说说备份:数据留存与管理实用指南 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory QQ空间数据备份是许多用户保存青春记忆和重要记录的需求。GetQzonehist…...
SpeedyStepper Forked:嵌入式步进电机硬实时控制库解析
1. SpeedyStepper Forked:面向嵌入式实时控制的高性能步进电机驱动库深度解析1.1 库定位与工程价值SpeedyStepper Forked 是一个专为嵌入式平台(尤其是基于Arduino生态的MCU)设计的轻量级、高精度步进电机运动控制库。其核心目标并非提供图形…...
Transformer在车道线检测中的实战应用:LSTR模型从理论到代码实现
Transformer在车道线检测中的实战应用:LSTR模型从理论到代码实现 自动驾驶技术的快速发展对车道线检测提出了更高要求。传统基于CNN的分割方法往往需要复杂的后处理流程,而LSTR(Lane Shape Prediction with Transformers)通过端到…...
CGAL-6.0.1在Win11与VS2019环境下的高效编译与配置指南
1. 环境准备:搭建Win11VS2019开发环境 在开始编译CGAL-6.0.1之前,我们需要确保开发环境配置正确。我实测发现,Win11系统与VS2019的组合存在一些特殊配置需求,这里分享几个关键检查点: 首先确认VS2019的安装组件。打开V…...
STS4x温度传感器I²C驱动库深度解析与跨平台移植
1. STS4x温湿度传感器驱动库技术解析1.1 项目定位与工程价值Sensirion STS4x系列是瑞士Sensirion公司推出的高精度数字温度传感器,采用CMOSens技术,具备0.1C典型精度、0.01C分辨率、低功耗(典型待机电流仅0.5μA)及快速响应&#…...
