今天分享自己在项目中用到的一个小技巧,就是使用 EntityFrameworkCore 时我们在通过代码去 Update-Database 生成数据库时如何自动将代码模型上的注释和字段上的注释携带到数据库中,方便后续在数据库直接查看各个表和各个字段的含义。

实现效果如下:

可以看到我们每张表都有明确的注释信息

选中表进入设计模式也可以直接看到各个字段注释

在查看表数据的时候,鼠标放在字段栏上同样也可以显示我们为字段设置的注释信息

我上面截图用的数据库管理工具是 Navicat ,各个数据库工具的呈现UI方式可能有所不同。


熟悉微软官方 EntityFrameworkCore 文档的小伙伴这个时候肯定会想到下面两个东西

当然直接为表或者模型手动指定 Comment 属性就可以实现我们上面的效果了,但是我们想要的并不是这样,因为我们在开发过程中往往给代码已经写过一次注释了,像下面的类

我们其实已经为 TOrder 模型写过注释了,甚至他内部的每个字段我们都写了注释,这样写注释的好处在于外部代码调用类时在代码编辑器中引用到模型或者字段时都可以显示注释信息出来,方便后续的代码维护。

有过同样经历的小伙伴这时候肯定就会想到,这边的注释没法直接带入数据库,我们今天要解决的就是这个问题,将代码上的注释自动赋值给 Comment 属性实现自动生成数据库表和字段的注释。

想要实现这点,首先我们需要为放置数据库模型类的代码类库启用 XML 文件生成,同时设置取消 1591 的警告,这个操作如果配置过 WebAPI Swagger 文档的小伙伴肯定很熟悉,其实都是一样的目的,就是为了项目在生成时自动生成模型的注释信息到XML文件中,因为注释信息我们的代码在编译的时候是会直接忽略的,所以并不能通过代码的某个属性来获取写在注释中的信息,所以我们选择开启 XML 描述文件生成,然后通过解析这个文件就可以获取到我们想要的注释信息。

可以在 visual studio 中选中类库右击属性,调整如下两个值

也可以直接选中类库后右击选择标记项目文件,编辑如下信息

<GenerateDocumentationFile>True</GenerateDocumentationFile>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1591</NoWarn>
</PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<NoWarn>1591</NoWarn>
</PropertyGroup>

准备工作完成之后,接下来是我们的 GetEntityComment 方法,这是一个静态方法,用于解析 XML 文件获取指定类和字段的注释,代码如下,我这里直接将这个方法写在了 DatabaseContext 里面,大家可以按照自己的喜好放置。

其中 path 就是我们类库文档xml文件的位置,我这里默认是项目当前目录下的,文件默认名称就是类库的名称,我这里是 Repository.xml ,大家需要按照自己的实际情况进行调整。

using System.Xml;

namespace Repository.Database
{
public class DatabaseContext : DbContext
{ public static string GetEntityComment(string typeName, string? fieldName = null, List<string>? baseTypeNames = null)
{
var path = Path.Combine(AppContext.BaseDirectory, "Repository.xml");
XmlDocument xml = new();
xml.Load(path);
XmlNodeList memebers = xml.SelectNodes("/doc/members/member")!; Dictionary<string, string> fieldList = new(); if (fieldName == null)
{
var matchKey = "T:" + typeName; foreach (object m in memebers)
{
if (m is XmlNode node)
{
var name = node.Attributes!["name"]!.Value; var summary = node.InnerText.Trim(); if (name == matchKey)
{
fieldList.Add(name, summary);
}
}
} return fieldList.FirstOrDefault(t => t.Key.ToLower() == matchKey.ToLower()).Value ?? typeName.ToString().Split(".").ToList().LastOrDefault()!;
}
else
{ foreach (object m in memebers)
{
if (m is XmlNode node)
{
string name = node.Attributes!["name"]!.Value; var summary = node.InnerText.Trim(); var matchKey = "P:" + typeName + ".";
if (name.StartsWith(matchKey))
{
name = name.Replace(matchKey, ""); fieldList.Remove(name); fieldList.Add(name, summary);
} if (baseTypeNames != null)
{
foreach (var baseTypeName in baseTypeNames)
{
if (baseTypeName != null)
{
matchKey = "P:" + baseTypeName + ".";
if (name.StartsWith(matchKey))
{
name = name.Replace(matchKey, "");
fieldList.Add(name, summary);
}
}
}
}
}
} return fieldList.FirstOrDefault(t => t.Key.ToLower() == fieldName.ToLower()).Value ?? fieldName;
}
}
} }

有了上面的方法我们就只要在对 DatabaseContext.OnModelCreating 方法稍加改造即可就能实现我们本次的目的。

我这里添加了 if DEBUG 标记用来控制只有在开发模式才会执行设置表备注和字段备注的代码,在线上运行时并不会进入这一部分逻辑

protected override void OnModelCreating(ModelBuilder modelBuilder)
{ foreach (var entity in modelBuilder.Model.GetEntityTypes())
{
modelBuilder.Entity(entity.Name, builder =>
{ #if DEBUG
//设置表的备注
builder.ToTable(t => t.HasComment(GetEntityComment(entity.Name))); List<string> baseTypeNames = new();
var baseType = entity.ClrType.BaseType;
while (baseType != null)
{
baseTypeNames.Add(baseType.FullName!);
baseType = baseType.BaseType;
}
#endif foreach (var property in entity.GetProperties())
{ #if DEBUG
//设置字段的备注
property.SetComment(GetEntityComment(entity.Name, property.Name, baseTypeNames));
#endif //设置字段的默认值
var defaultValueAttribute = property.PropertyInfo?.GetCustomAttribute<DefaultValueAttribute>();
if (defaultValueAttribute != null)
{
property.SetDefaultValue(defaultValueAttribute.Value);
}
}
});
}
}

这样就算完成了,我们尝试去执行 Add-Migration 命令,然后观察生成的文件,就会发现已经包含我们的注释信息了,然后直接 Update-Database 推送到数据库中即可。

至此关于 小技巧 EntityFrameworkCore 实现 CodeFirst 通过模型生成数据库表时自动携带模型及字段注释信息 就讲解完了,有任何不明白的,可以在文章下面评论或者私信我,欢迎大家积极的讨论交流,有兴趣的朋友可以关注我目前在维护的一个 .NET 基础框架项目,项目地址如下

https://github.com/berkerdong/NetEngine.git

https://gitee.com/berkerdong/NetEngine.git

小技巧 EntityFrameworkCore 实现 CodeFirst 通过模型生成数据库表时自动携带模型及字段注释信息的更多相关文章

  1. Django 生成数据库表时的报错TypeError: __init__() missing 1 required positional argument: 'on_delete'

    原因及解决办法: https://www.cnblogs.com/phyger/p/8035253.html

  2. VS2010 根据模型生成数据库 打开edmx.sql文件时 vs出现无响应的解决方案

    今天在VS2010 sp1+sql server 2008 R2+Win7操作系统下测试ADO.NET 实体数据模型时 ,遇到这样一个问题. 首先建好实体模型,然后"根据模型生成数据库&qu ...

  3. 【工具篇】利用DBExportDoc V1.0 For MySQL自动生成数据库表结构文档

    对于DBA或开发来说,如何规范化你的数据库表结构文档是灰常之重要的一件事情.但是当你的库,你的表排山倒海滴多的时候,你就会很头疼了. 推荐一款工具DBExportDoc V1.0 For MySQL( ...

  4. PowerDesigner生成数据库表和逆向生成表结构(MySQL数据库)

    一.Download Connector/ODBC下载ODBC驱动,地址:https://dev.mysql.com/downloads/connector/odbc/, 需要注意:PowerDesi ...

  5. NetCore使用使用Scaffold-DbContext命令生成数据库表实体类

    一.为了模拟项目,本处创建了一个NetCore的Web项目.打算在Models文件夹下生成数据库表的实体类. 二.在程序包管理控制台,输入“Scaffold-DbContext "Serve ...

  6. hibernate笔记--通过SchemaExport生成数据库表

    方法比较简单,项目中只需要两个java类(一个实体类,如User,一个工具类),两个配置文件(hibernate必须的两个配置文件hibernate.cfg.xml,与User.hbm.xml),即可 ...

  7. (喷血分享)利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句

    (喷血分享)利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句 在我们RDIFramework.NET代码生成器中,有这样一个应用,就是通过数据库表自动生成表的CREA ...

  8. 利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句

    利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句 (喷血分享)利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句 在我们RDIFram ...

  9. Hibernate生成数据库表

    首先创建实体类 import java.util.Date; public class ProductionEntity { public Integer getId() { return id; } ...

  10. Python3:Django根据models生成数据库表时报 __init__() missing 1 required positional argument: 'on_delete'

    Python3:Django根据models生成数据库表时报 __init__() missing 1 required positional argument: 'on_delete' 一.分析 在 ...

随机推荐

  1. Beats & FileBeat

    Beats是一个开放源代码的数据发送器.我们可以把Beats作为一种代理安装在我们的服务器上,这样就可以比较方便地将数据发送到Elasticsearch或者Logstash中.Elastic Stac ...

  2. 我是加工厂,想管理生产财务采购销售这块,什么样的ERP会好用点??

    最能够贴合你的业务需求和自己员工的使用习惯的才会更好用,最好能简单快捷的进行低成本个性化定制的那种应该比较适合你这种加工厂,没有完全相同的两家企业,更别说他们的发展走向,即使同一家企业不同发展阶段.时 ...

  3. 为什么数字化转型离不开 MES 系统?

    确切的说应该是制造业企业的数字化转型离不开MES系统,原因很简单,制造业企业的核心工作是生产制造,做数字化转型就是对生产制造各个环节进行数字化改造,提质增效降成本,而MES系统是制造执行系统,是生产制 ...

  4. Jmeter——BeanShell 内置变量vars、props、prev的使用

    在使用Jmeter过程中,或多或少都会接触些BeanShell,它会使工具的使用,变得更灵活. Jmeter中关于BeanShell的有: 1.BeanShell Sampler 取样器:完成Bean ...

  5. RAID5 IO处理之条带读代码详解

    除了对齐读流程中读失败通过条带重试的场景会进入到条带读,当IO覆盖范围超过一个chunk时也会进入条带读(如向chunk为4K的RAID下发起始位置为1K大小为4K的IO),接下来我们就这部分逻辑进行 ...

  6. 6.RabbitMQ系列之direct直连交换器

    我们通过fanout exchange(扇型交换机)实现生产者发送一个消息,这个消息同时被传送给所有队列.但是有时我们不希望所有的消息都被所有队列接收,我们希望可以指定类型为a的消息只能被队列A接收, ...

  7. 20220729 - DP训练 #2

    20220729 - DP训练 #2 时间记录 \(8:00-8:10\) 浏览题面 \(8:10-8:50\) T1 看题想到了建树,从每一个点遍历,若能遍历每一个点,则可以获胜 快速写完之后,发现 ...

  8. 为了讲明白继承和super、this关键字,群主发了20块钱群红包

    摘要:以群主发红包为例,带你深入了解继承和super.this关键字. 本文分享自华为云社区<群主发红包带你深入了解继承和super.this关键字>,作者:共饮一杯无 . 需求 群主发随 ...

  9. Python--网络编程学习笔记系列02 附:tcp服务端,tcp客户端

    Python--网络编程学习笔记系列02 TCP和UDP的概述: udp通信模型类似于写信,不需要建立相关链接,只需要发送数据即可(现在几乎不用:不稳定,不安全) tcp通信模型类似于打电话,一定要建 ...

  10. Redisson源码解读-分布式锁

    前言 Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid).Redisson有一样功能是可重入的分布式锁.本文来讨论一下这个功能的特点以及源 ...