• Day 2 Abp框架下,MySQL数据迁移时,添加表和字段注释


    后端采用Abp框架,当前最新版本是7.4.0。

    数据库使用MySQL,在执行数据库迁移时,写在Domain层的Entity类上的注释通通都没有,这样查看数据库字段的含义时,就需要对照代码来看,有些不方便。今天专门来解决这个问题。

    还是一顿搜索,发现了两个方案:

    abp 框架拓展mysql 迁移:增加数据库表和列备注

    EFcore+MySql 数据迁移的时候,怎么给表结构加注释?


     

    上述两篇文章,

    第一篇重载 MySqlMigrationsSqlGenerator 来实现加注释,但是字段注释是通过Description属性来获取的,这样字段上就要注释和Description重复写两遍。

    第二篇直接通过工具,读取xml文档,生成 HasComment相关代码,每次都需要手动修改DbContext代码。

    都不是特别完美,所以结合一下看看。具体方案还是重载MySqlMigrationsSqlGenerator,但是通过读取xml来获取信息。

    首先是SqlGenerator:

    1. ///
    2. /// 拓展迁移操作:增加数据表和列备注
    3. ///
    4. public class MyMigrationsSqlGenerator : MySqlMigrationsSqlGenerator
    5. {
    6. public MyMigrationsSqlGenerator(
    7. MigrationsSqlGeneratorDependencies dependencies,
    8. IMigrationsAnnotationProvider migrationsAnnotations,
    9. ICommandBatchPreparer commandBatchPreparer,
    10. IMySqlOptions mySqlOptions)
    11. : base(dependencies, commandBatchPreparer, mySqlOptions)
    12. {
    13. }
    14. protected override void Generate(MigrationOperation operation, IModel model, MigrationCommandListBuilder builder)
    15. {
    16. base.Generate(operation, model, builder);
    17. if (operation is CreateTableOperation || operation is AlterTableOperation)
    18. CreateTableComment(operation, model, builder);
    19. if (operation is AddColumnOperation || operation is AlterColumnOperation)
    20. CreateColumnComment(operation, model, builder);
    21. }
    22. ///
    23. /// 创建表注释
    24. ///
    25. ///
    26. ///
    27. private void CreateTableComment(MigrationOperation operation, IModel model, MigrationCommandListBuilder builder)
    28. {
    29. string tableName = string.Empty;
    30. string description = string.Empty;
    31. if (operation is AlterTableOperation)
    32. {
    33. var t = operation as AlterColumnOperation;
    34. tableName = (operation as AlterTableOperation).Name;
    35. }
    36. if (operation is CreateTableOperation)
    37. {
    38. var t = operation as CreateTableOperation;
    39. var addColumnsOperation = t.Columns;
    40. tableName = t.Name;
    41. foreach (var item in addColumnsOperation)
    42. {
    43. CreateColumnComment(item, model, builder);
    44. }
    45. }
    46. //description = DbDescriptionHelper.GetDescription(tableName.Replace(jingdianConsts.DbTablePrefix, ""));
    47. description = GetDescription(tableName, null);
    48. if (tableName.IsNullOrWhiteSpace())
    49. throw new Exception("表名为空引起添加表注释异常.");
    50. var sqlHelper = Dependencies.SqlGenerationHelper;
    51. builder
    52. .Append("ALTER TABLE ")
    53. .Append(sqlHelper.DelimitIdentifier(tableName))
    54. .Append(" COMMENT ")
    55. .Append("'")
    56. .Append(description)
    57. .Append("'")
    58. .AppendLine(sqlHelper.StatementTerminator)
    59. .EndCommand();
    60. }
    61. ///
    62. /// 创建列注释
    63. ///
    64. ///
    65. ///
    66. private void CreateColumnComment(MigrationOperation operation, IModel model, MigrationCommandListBuilder builder)
    67. {
    68. //alter table a1log modify column UUID VARCHAR(26) comment '修改后的字段注释';
    69. string tableName = string.Empty;
    70. string columnName = string.Empty;
    71. string columnType = string.Empty;
    72. string description = string.Empty;
    73. if (operation is AlterColumnOperation)
    74. {
    75. var t = (operation as AlterColumnOperation);
    76. columnType = t.ColumnType;
    77. }
    78. if (operation is AddColumnOperation)
    79. {
    80. var t = (operation as AddColumnOperation);
    81. columnType = t.ColumnType;
    82. description = GetDescription(tableName, columnName);
    83. }
    84. if (columnName.IsNullOrWhiteSpace() || tableName.IsNullOrWhiteSpace() || columnType.IsNullOrWhiteSpace())
    85. throw new Exception("列名为空或表名为空或列类型为空引起添加列注释异常." + columnName + "/" + tableName + "/" + columnType);
    86. var sqlHelper = Dependencies.SqlGenerationHelper;
    87. builder
    88. .Append("ALTER TABLE ")
    89. .Append(sqlHelper.DelimitIdentifier(tableName))
    90. .Append(" MODIFY COLUMN ")
    91. .Append(columnName)
    92. .Append(" ")
    93. .Append(columnType)
    94. .Append(" COMMENT ")
    95. .Append("'")
    96. .Append(description)
    97. .Append("'")
    98. .AppendLine(sqlHelper.StatementTerminator)
    99. .EndCommand();
    100. }
    101. private string GetDescription(string tableName, string? columnName)
    102. {
    103. var type = TableCommentRegister.Types[tableName];
    104. if (type == null)
    105. {
    106. return string.Empty;
    107. }
    108. string xmlPath = type.Module.Name.Replace("dll","xml");
    109. XmlDocument xml = new XmlDocument();
    110. xml.Load(xmlPath);
    111. var classNode = xml.SelectSingleNode(($"//member[@name='T:{type.FullName}']"));
    112. if (classNode == null)
    113. {
    114. return string.Empty;
    115. }
    116. if (columnName == null)
    117. {
    118. return classNode.InnerText.Trim();
    119. }
    120. else
    121. {
    122. var propertyNode = xml.SelectSingleNode(($"//member[@name='P:{type.FullName}.{columnName}']"));
    123. if (propertyNode == null)
    124. {
    125. return string.Empty;
    126. }
    127. return propertyNode.InnerText.Trim();
    128. }
    129. }
    130. }

    这里面有一个点,生成Sql时,只知道table name和column name,一般情况下列名就是属性名,可以不考虑,但是表名和类名可能会有差异,比如前后缀之类的。参考1中,就是直接做替换,我考虑还是做了一个静态字典对象,把表名和类名做了一个映射,具体如下:

    1. ///
    2. /// 数据表注册器
    3. ///
    4. public static class TableCommentRegister
    5. {
    6. public static Dictionary<string, Type> Types { get; set; } = new Dictionary<string, Type>();
    7. ///
    8. /// 配置实体对应的数据表,同时注册类型,用于后续生成备注
    9. ///
    10. /// 实体类型
    11. ///
    12. /// 数据表名
    13. public static void ToTableWithComment<T>(this EntityTypeBuilder builder, string tableName) where T : class
    14. {
    15. builder.ToTable(tableName);
    16. Types.TryAdd(tableName, typeof(T));
    17. }
    18. }

    然后在DbContext类中,针对表的处理代码如下:

    1. builder.Entity(b =>
    2. {
    3. string tableName = Consts.DbTablePrefix + "Company";
    4. b.ToTableWithComment(tableName);
    5. b.ConfigureByConvention(); //auto configure for the base class props
    6. });

    就是把之前的 ToTable 改成 ToTableWithComment 就可以了。

    最后,需要修改DbSchemaMigrator类,把SqlGenerator注册进去。这里我就简单粗暴的复制了一下DbContextFactory类。因为DbContextFactory代码注释则表明了其只是用于EF Core console commands,在Abp的DbMigrator程序中不起作用。

    DbSchemaMigrator类中,Abp 脚手架代码应该是这样的:

    1. public async Task MigrateAsync()
    2. {
    3. /* We intentionally resolving the XiuYuanDbContext
    4. * from IServiceProvider (instead of directly injecting it)
    5. * to properly get the connection string of the current tenant in the
    6. * current scope.
    7. */
    8. await _serviceProvider
    9. .GetRequiredService()
    10. .Database
    11. .MigrateAsync();
    12. }

    修改如下:

    1. public async Task MigrateAsync()
    2. {
    3. await CreateDbContext()
    4. .Database
    5. .MigrateAsync();
    6. }
    7. public xxxDbContext CreateDbContext()
    8. {
    9. xxxEfCoreEntityExtensionMappings.Configure();
    10. var configuration = BuildConfiguration();
    11. var connection = configuration.GetConnectionString(xxxConsts.DbSchema);
    12. var builder = new DbContextOptionsBuilder()
    13. .UseMySql(connection, ServerVersion.AutoDetect(connection), o => o.SchemaBehavior(MySqlSchemaBehavior.Ignore))
    14. // 注意这里的ReplaceService
    15. .ReplaceService();
    16. return new xxxDbContext(builder.Options);
    17. }
    18. private static IConfigurationRoot BuildConfiguration()
    19. {
    20. var builder = new ConfigurationBuilder()
    21. .AddJsonFile("appsettings.json", optional: false);
    22. return builder.Build();
    23. }

    至此,所有基础性工作都完成了,后面再添加领域模型时,记得把ToTable改成ToTableWithComment即可。

  • 相关阅读:
    【Python】
    react native中使用Animated实现三张图片动态旋转效果
    std::apply 源码分析
    【数据结构】二叉树
    湖南首个,万应低代码软件技术专业校企共建基地落成!
    几种典型的深度学习算法:(CNN、RNN、GANS、RL)
    找搭建app的朋友,要求什么都会。
    web前端面试高频考点——Vue面试题
    数仓建模分层理论
    HTML整站规划与规范
  • 原文地址:https://blog.csdn.net/lee_leefox/article/details/134031119