zoukankan      html  css  js  c++  java
  • 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 在开源化的今天,我们只要愿意去多翻翻源码,会发现自己可以扩展的东西还有很多~很多~

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

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

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

    作者:顾振印 出处:http://www.cnblogs.com/GuZhenYin/ 如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面
  • 相关阅读:
    软件设计中的立足点
    Clojure基础
    团队凝聚力
    执行力与领导力
    工作与生活
    分离焦虑OR责任焦虑
    保持激情
    立足点
    论研发管理--开篇
    初级码农常犯错误
  • 原文地址:https://www.cnblogs.com/GuZhenYin/p/15012296.html
Copyright © 2011-2022 走看看