EFCore Database-first深入研究

使用Scaffold-DbContext从数据库生成实体

说明文档:

关于 Scaffold-DbContext 微软有官方说明文档

https://docs.microsoft.com/zh-cn/ef/core/miscellaneous/cli/powershell

实践:

不妨自己找一个现有的数据库试一试:

LocalDB

Scaffold-DbContext "Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Tables "Blog","Post" -ContextDir Context -Context BlogContext

MySql

Scaffold-DbContext "server=127.0.0.1;Port=3306;database=zejedb;uid=root;pwd=123456;Convert Zero Datetime=True;Allow Zero Datetime=True;Charset=utf8;Default Command Timeout =30000;" Pomelo.EntityFrameworkCore.Mysql -OutputDir Models -DataAnnotations

路漫漫

但是通过上述步骤生成的实体,跟自己想要的还是有那么一丢丢差距。

  • 比如我想通过数据库表注释、字段注释,给实体、属性加上注释貌似就没办法实现。
  • 比如我想统一继承基类,将创建时间、修改时间、创建人、修改人、Id放到基类,貌似也没办法实现。

作为一个被抛弃的10年neter,对于自己未曾研究过源码耿耿于怀(其实n年前我研究过jquery的部分源码,那不算)。因此我毅然下载了EFCore的源码。

源码地址:https://github.com/dotnet/efcore.git

研究EFCore的源码

以Scaffold-DbContext作为突破口:

  • 后来又看到这么几个类:

    • DatabaseModelFactory
    • SqlServerDatabaseModelFactory
    • DatabaseModel
  • 还下载了Pomelo.EntityFrameworkCore.MySql的源码
  • 等等

皇天不负有心人:用了一个周末的时间,关于如何添加注释,我找到了最关键地方

CSharpEntityTypeGenerator

只要重写(Override)此类的GenerateClass,GenerateProperties两个虚拟方法便可

吐槽&继续(痛苦并快乐着)

微软的攻城狮细节怎么就不处理好呢?

    public class CSharpEntityTypeGenerator : ICSharpEntityTypeGenerator
{
private readonly ICSharpHelper _code; private IndentedStringBuilder _sb = null!;
private bool _useDataAnnotations;

关键方法:

    public virtual string WriteCode(IEntityType entityType, string @namespace, bool useDataAnnotations)
{
Check.NotNull(entityType, nameof(entityType));
Check.NotNull(@namespace, nameof(@namespace)); _sb = new IndentedStringBuilder();
_useDataAnnotations = useDataAnnotations; _sb.AppendLine("using System;");
_sb.AppendLine("using System.Collections.Generic;"); if (_useDataAnnotations)
{
_sb.AppendLine("using System.ComponentModel.DataAnnotations;");
_sb.AppendLine("using System.ComponentModel.DataAnnotations.Schema;");
} foreach (var ns in entityType.GetProperties()
.SelectMany(p => p.ClrType.GetNamespaces())
.Where(ns => ns != "System" && ns != "System.Collections.Generic")
.Distinct()
.OrderBy(x => x, new NamespaceComparer()))
{
_sb.AppendLine($"using {ns};");
} _sb.AppendLine();
_sb.AppendLine($"namespace {@namespace}");
_sb.AppendLine("{"); using (_sb.Indent())
{
GenerateClass(entityType);
} _sb.AppendLine("}"); return _sb.ToString();
}

生成实体全然靠WriteCode方法,为什么sb不公开?(后来才知道,他们合并代码有这么一个校验:Public_inheritable_apis_should_be_virtual)

无奈之下,我把整个类都拷贝出来,稍微修改,添加上关键代码

  • 类注释:
var comment = entityType.FindAnnotation("Relational:Comment");
if (comment != null && comment.Value != null)
{
_sb.AppendLine("///<summary>");
_sb.AppendLine("///" + comment.Value.ToString());
_sb.AppendLine("///</summary>");
}
  • 实体注释:
var comment = property.FindAnnotation("Relational:Comment");
if (comment != null && comment.Value != null)
{
_sb.AppendLine("///<summary>");
_sb.AppendLine("///" + comment.Value.ToString());
_sb.AppendLine("///</summary>");
}
  • 测试结果:

总结

生成实体的工具那么多,何必纠缠Scaffold-DbContext不放手?

其实:

1、个人有代码洁癖。

2、有代码注释强迫症。

3、给自己找个强迫自己看源码的理由。

其实实现自动生成代码并带上注释的,个人已经有实现的办法,但是不完美,有些地方还是需要手动修改。纠缠Scaffold-DbContext是另外一种尝试罢了。

放弃Scaffold-DbContext,使用T4模板

为何放弃

难以自定义

  • 无法带注释(上述几行代码已在github上推送了),不过Scaffold-DbContext要支持注释估计是猴年马月的事情了。
  • 不知道怎么继承基类等

PackageReferences在T4模板下的水土不服

使用CopyLocalLockFileAssemblies,将dll复制到bin目录下

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
</ItemGroup>
</Project>

T4模板中引用DLL

<#@ assembly name="$(TargetDir)\Microsoft.EntityFrameworkCore.dll" #>
<#@ assembly name="$(TargetDir)\Microsoft.EntityFrameworkCore.Design.dll" #>

其实本人之前是使用自己写的Zeje.T4_.dll来生成的,现在计划基于Microsoft.EntityFrameworkCore.dll及其扩展类库进行研究,下个周末继续研究。

EFCore Database-first深入研究的更多相关文章

  1. MS SQL 统计信息浅析上篇

    统计信息概念 统计信息是一些对象,这些对象包含在表或索引视图中一列或多列中的数据分布有关的统计信息.数据库查询优化器使用这些统计信息来估计查询结果中的基数或行数. 通过这些基数估计,查询优化器可以生成 ...

  2. Entity Framework 之存储过程篇

    最近几天在搞CRUD,使用的是EF这个ORM,最近的项目中上了存储过程,就把在开发中的经验分享出来!我们先创建一个最基本的存储过程,脚本如下,这是一个不带参数的存储过程,我们从最简单的往上走! cre ...

  3. SAS数据挖掘实战篇【一】

    SAS数据挖掘实战篇[一] 1数据挖掘简介 1.1数据挖掘的产生 需求是一切技术之母,管理和计算机技术的发展,促使数据挖掘技术的诞生.随着世界信息技术的迅猛发展,信息量也呈几何指数增长,如何从巨量.复 ...

  4. DotNetCore跨平台~EFCore废弃了TransactionScope取而代之的Context.Database.BeginTransaction

    回到目录 TransactionScope是.net平台基于的分布式事务组件,它默认为本地事务,同时当系统有需要时可以自动提升为分布式事务,而对系统的前提是要开启MSDTC服务,必要时需要在数据库服务 ...

  5. Maclean Liu对Oracle Database 12c新特性研究汇总

    Maclean Liu关于DB 12c新特性的研究文章如下: [Oracle Database 12c新特性] In-Database Archiving数据库内归档 [Oracle Database ...

  6. 药物动力学|肿瘤药物基因组研究的策略|OMIM database|PharmGKB

    生命组学 同义突变虽然不改变蛋白质种类,但是影响量,修饰的稳定性. SNP vs mutation SNV单核苷酸变化,mutation,SNP是从群体角度思考的,约有1%,mutation比SNP还 ...

  7. Database事件研究

    1.Database.ObjectAppended.ObjectModified.ObjectErased事件 此事件如果不是Transaction提交而触发的,那么可以在事件内部使用Transact ...

  8. .NetCore技术研究-EntityFramework Core 3.0 Preview

    前段时间.Net Core 3.0 发布了,Entity Framework Core 3.0 也发布了Preview版.假期用了一上午大致研究了一遍,同时又体验了一把Visual Studio 20 ...

  9. 如何从40亿整数中找到不存在的一个 webservice Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库 WPF实战案例-打印 RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

    如何从40亿整数中找到不存在的一个 前言 给定一个最多包含40亿个随机排列的32位的顺序整数的顺序文件,找出一个不在文件中的32位整数.(在文件中至少确实一个这样的数-为什么?).在具有足够内存的情况 ...

随机推荐

  1. Laravel 5.* 执行seeder命令出现错误的解决方法

    最近在使用Laravel开发一个项目,测试中需要增加数据库基础数据动作,当第一次执行完`php artisan db:seed` 后,增加新的seeder文件时执行会报错.错误信息如下`[Reflec ...

  2. Python--day37--守护进程和几个常用的方法

    1,p.daemon = True #设置子进程为守护进程 #守护进程会随着主进程的代码执行完毕 而结束 #子进程 --> 守护进程 import time from multiprocessi ...

  3. java 菜单

    继承体系 MenuBar,Menu,MenuItem之间的关系: 先创建菜单条,再创建菜单,每一个菜单中建立菜单项. 也可以菜单添加到菜单中,作为子菜单. 通过setMenuBar()方法,将菜单添加 ...

  4. element-ui tree 根据不同叶子节点设置是否显示复选框

    公司业务要求不同根节点配置显示与否复选框,官方文档没有这样的配置,所以想到了修改element-ui源码. 1.这里将“node_modules\element-ui\packages”下的tree文 ...

  5. P1022 绵羊排序

    题目描述 聪聪想要给他家农场里的 \(n\) 只绵羊按照品质从高到低进行排序. 农场里的 \(n\) 只绵羊编号从 \(1\) 到 \(n\) ,第 \(i\) 只绵羊的体重为 \(w_i\) ,高度 ...

  6. 为什么阿里代码规约要求避免使用 Apache BeanUtils 进行属性复制

    缘起 有一次开发过程中,刚好看到小伙伴在调用 set 方法,将数据库中查询出来的 Po 对象的属性拷贝到 Vo 对象中,类似这样: 可以看出,Po 和 Vo 两个类的字段绝大部分是一样的,我们一个个地 ...

  7. CF1063D Candies for Children

    CF1063D Candies for Children 分类讨论题 n<=1e11, 整体上先分n<=2e6与否讨论 len长度,ans贪心的人,p就是len这一段贪心的人 n<= ...

  8. vue中处理时间格式化的问题

    vue main.js中修改Date原型链,插入(百度) Date.prototype.format = function(fmt) { var o = { "M+" : this ...

  9. 深浅Copy的理解

    在python里对对象进行拷贝有三个,即赋值,深拷贝,浅拷贝. 而对象又分为两种,一种是不可变对象(字符串.元组.数字 ),一种是可变对象(列表.字典). 而深浅拷贝有三种情况:拷贝的是不可变对象.拷 ...

  10. C# 线程参数

    . class ThreadSample { private readonly int _iterations; public ThreadSample(int iterations) { _iter ...