EF Core3.1 CodeFirst动态自动添加表和字段的描述信息

前言

我又来啦..

本篇主要记录如何针对CodeFirst做自动添加描述的扩展

为什么要用这个呢.. 因为EF Core3.1 CodeFirst 对于自动添加描述这块 只有少部分的数据库支持..

然而我们的客户大佬们 对这个又有要求..所以..没办法 只能自己扩展~

当然也可以根据这个原理来做一些有意思的扩展~

本文就以不支持的达梦数据库来举个栗子

.(PS:真心希望达梦数据库能开放EF Core相关的源码,这样我们也好提交点贡献,国产数据库还是不能太过敝帚自珍阿..)

 

 

 

正文

1.通过扩展生成器,来实现动态自动添加描述信息

我们知道在SQL Server中,可以通过Fluent API来添加针对表或者字段的描述,如下:

builder.Property(prop.Name)
    .HasComment("XXX字段描述");

然而在达梦的上下文中,我们如果这样写..是没任何效果的..不用想,肯定是达梦的开发商没写(很多扩展类都缺斤少两)..

那就需要我们自己扩展了, 所以就少不了翻看EF Core源码..

我们通过翻看源码,可以找到MigrationsSqlGenerator这个类. 类名翻译过来,喔唷,这不就是迁移SQL生成器么

那么我们就需要去实现他啦.首先,我们找到达梦实现他的子类:DmMigrationsSqlGenerator

通过反编译,我们发现,果然他并没实现对于Comment属性的代码,那么我们就需要自行扩展

我们添加MyDmigrationsSqlGenerator类继承DmMigrationsSqlGenerator 添加扩展代码如下:

  1 using Microsoft.EntityFrameworkCore.Metadata;
  2 using Microsoft.EntityFrameworkCore.Migrations;
  3 using Microsoft.EntityFrameworkCore.Migrations.Operations;
  4 using System;
  5 using System.Collections.Generic;
  6 using System.Diagnostics.CodeAnalysis;
  7 using System.Linq;
  8 using System.Text;
  9 
 10 namespace Ciac.ZTBExpert.Model
 11 {
 12     public class MyDmigrationsSqlGenerator : DmMigrationsSqlGenerator
 13     {
 14         public MyDmigrationsSqlGenerator([NotNull] MigrationsSqlGeneratorDependencies dependencies, [NotNull] IMigrationsAnnotationProvider migrationsAnnotations)
 15         : base(dependencies, migrationsAnnotations)
 16         {
 17 
 18         }
 19 
 20         protected override void Generate(
 21            CreateTableOperation operation,
 22            IModel model,
 23            MigrationCommandListBuilder builder,
 24            bool terminate)
 25         {
 26             base.Generate(operation, model, builder, terminate);
 27             var comment = operation.Comment;
 28             if (comment != null)
 29             {
 30                 if (terminate)
 31                 {
 32                     builder.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
 33                     EndStatement(builder);
 34                 }
 35                 builder
 36                     .Append("COMMENT ON TABLE ")
 37                     .Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name, operation.Schema))
 38                     .Append(" IS ")
 39                     .Append($"'{comment}'")
 40                     .Append(Dependencies.SqlGenerationHelper.StatementTerminator);
 41             }
 42             // Comments on the columns
 43             foreach (var columnOp in operation.Columns.Where(c => c.Comment != null))
 44             {
 45                 var columnComment = columnOp.Comment;
 46                // builder.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
 47                 EndStatement(builder);
 48                 builder
 49                     .Append("COMMENT ON COLUMN ")
 50                     .Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name, operation.Schema))
 51                     .Append('.')
 52                     .Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(columnOp.Name))
 53                     .Append(" IS ")
 54                     .Append($"'{columnComment}'")
 55                     .Append(Dependencies.SqlGenerationHelper.StatementTerminator);
 56             }
 57             builder.EndCommand();
 58         }
 59 
 60 
 61         protected override void Generate(AlterColumnOperation operation, IModel model, MigrationCommandListBuilder builder)
 62         {
 63             base.Generate(operation, model, builder);
 64             // Comment
 65             var oldComment = operation.OldColumn.Comment;
 66             var newComment = operation.Comment;
 67 
 68             if (oldComment != newComment)
 69             {
 70                 //builder.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
 71 
 72                 builder
 73                     .Append("COMMENT ON COLUMN ")
 74                     .Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Table, operation.Schema))
 75                     .Append('.')
 76                     .Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name))
 77                     .Append(" IS ")
 78                     .Append($"'{newComment}'")
 79                     .Append(Dependencies.SqlGenerationHelper.StatementTerminator);
 80                 builder.EndCommand();
 81             }
 82 
 83         }
 84 
 85         protected override void Generate(AddColumnOperation operation, IModel model, MigrationCommandListBuilder builder, bool terminate)
 86         {
 87             base.Generate(operation, model, builder, terminate);
 88             // Comment
 89             var newComment = operation.Comment;
 90 
 91             if (!string.IsNullOrEmpty(newComment))
 92             {
 93                // builder.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
 94                 builder
 95                     .Append("COMMENT ON COLUMN ")
 96                     .Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Table, operation.Schema))
 97                     .Append('.')
 98                     .Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name))
 99                     .Append(" IS ")
100                     .Append($"'{newComment}'")
101                     .Append(Dependencies.SqlGenerationHelper.StatementTerminator);
102                 if (terminate)
103                 {
104                     EndStatement(builder);
105                 }
106                 builder.EndCommand();
107 
108             }
109 
110         }
111         protected override void Generate([NotNull] AlterTableOperation operation, IModel? model, [NotNull] MigrationCommandListBuilder builder)
112         {
113             base.Generate(operation, model, builder);
114             // Comment
115             var oldComment = operation.OldTable.Comment;
116             var newComment = operation.Comment;
117 
118             if (oldComment != newComment)
119             {
120                 
121                 EndStatement(builder);
122 
123                 builder
124                     .Append("COMMENT ON TABLE ")
125                     .Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name, operation.Schema))
126                     .Append(" IS ")
127                     .Append($"'{newComment}'")
128                     .Append(Dependencies.SqlGenerationHelper.StatementTerminator);
129                 builder.EndCommand();
130             }
131         }
132     }
133 }

 

因为我们只是想在创建或者修改表后添加描述.

所以,我们只需要针对CreateTable,AlterColumn,AddColumn,AlterTable 四个生成方法做重写就好了

最后我们需要在EF上下文初始化之前来替换掉原来的生成器如下:

 

 

这样,我们就可以通过在上下文中配置Fluent API就可以自动生成描述了~

我们在EF上下文的OnModelCreating添加代码如下:

 protected override void OnModelCreating(ModelBuilder modelBuilder)
 {
            modelBuilder.Entity<tab_zjcq_ggxx>(a => a.Property("aaa").HasComment("88888"));
}

执行迁移语句Script-Migration

结果如下:

ALTER TABLE "tab_zjcq_ggxx" MODIFY "aaa" NVARCHAR2(50) NULL;


/COMMENT ON COLUMN "tab_zjcq_ggxx"."aaa" IS '8888';

 

2.通过添加Description特性来优化代码风格,方便管理

虽然上面第一步就已经实现了我们的要求,但是我们发现,通过Fluent API 来添加描述,代码可读性会很差,

且一旦表多起来,那么OnModelCreating 方法就会变的超长(虽然也可以写在实体类里面,但是就觉得很麻烦)..

那么能不能像[MaxLength(50)] 这种特性一样,直接在字段上加个特性来解决这个事情呢?~

当然是可以的啦~

我们修改OnModelCreating 中的代码如下:

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            
            var ddd= modelBuilder.Model.GetEntityTypes().ToList();
            foreach (var item in ddd)
            {
                var tabtype = Type.GetType(item.ClrType.FullName);
                var props = tabtype.GetProperties();
                var descriptionAttrtable = tabtype.GetCustomAttributes(typeof(DescriptionAttribute), true);
                if (descriptionAttrtable.Length > 0)
                {
                    modelBuilder.Entity(item.Name).HasComment(((DescriptionAttribute)descriptionAttrtable[0]).Description);
                }
                foreach (var prop in props)
                {
                    var descriptionAttr = prop.GetCustomAttributes(typeof(DescriptionAttribute), true);
                    if (descriptionAttr.Length>0)
                    {
                        modelBuilder.Entity(item.Name).Property(prop.Name).HasComment(((DescriptionAttribute)descriptionAttr[0]).Description);
                    }
                }
            }

        }

这里通过反射,得到包含DescriptionAttribute特性的字段,然后读取描述信息,通过HasComment 自动添加~

然后我们给字段添加描述如下:

 

 

执行迁移语句Script-Migration~

我们会发现,描述已经自动生成啦~

 

结束语

其实不管是.NET 5.0 还是EF Core 在开源化的今天,我们只要愿意去多翻翻源码,会发现自己可以扩展的东西还有很多~很多~

最后..在提一嘴,真心希望国产数据库的访问库 能够开源.. 毕竟,人多力量大~

又不需要数据库应用开源..起码访问组件 你能开源吧..

好了..就到这了 瑞思拜~

 

posted @ 2021-07-14 18:10  GuZhenYin  阅读(5636)  评论(19编辑  收藏  举报
.........................................作者:顾振印出处:http://www.cnblogs.com/GuZhenYin/本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面