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 在開源化的今天,我們只要願意去多翻翻源碼,會發現自己可以擴展的東西還有很多~很多~

最后..在提一嘴,真心希望國產數據庫的訪問庫 能夠開源.. 畢竟,人多力量大~

又不需要數據庫應用開源..起碼訪問組件 你能開源吧..

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM