4

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

 3 years ago
source link: https://www.cnblogs.com/GuZhenYin/p/15012296.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

我又来啦..

本篇主要记录如何针对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 方法就会变的超长(虽然也可以写在实体类里面,但是就觉得很麻烦)..653851-20210714180146866-2032726892.png

那么能不能像[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 在开源化的今天,我们只要愿意去多翻翻源码,会发现自己可以扩展的东西还有很多~很多~

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

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

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

653851-20210714180959351-44075931.png


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK