一、概览
二、反向工程(Dbfirst根据数据库表来反向生成实体类)
- 定义:根据数据库已有表反向生成实体类
- 使用场景:新项目但是需要使用已存在数据库的旧表。
-
命令行脚本:两种
命令行脚本1:Scaffold-Dbcontext "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook" Microsoft.EntityFrameworkCore.SqlServer
命令行脚本2(官网):dotnet ef dbcontext scaffold "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook" Microsoft.EntityFrameworkCore.SqlServer
例子:
Scaffold-DbContext "server=DESKTOP-E3D2J8F\SQLEXPRESS;Database=ImportExcelMVCTest; user id=sa; Password=123456;TrustServerCertificate=true" Microsoft.EntityFrameworkCore.SqlServer -
生成结果:
1、数据库下面所有表都会一一对应生成实体类。
2、生成一个继承DbContext的类,文件命名为:数据库名+Context -
生成的代码文件可能会不符合预期,但是可以手动进行修改:
1、生成的实体类的类名可能不符合预期,例如数据库中的表:T_Books,生成的类名是TBook,去掉了 ”_“ 和结尾的 s ,可手动修改成预期的类名,比如改成Book。
2、所有的配置信息没有针对每个实体类生成一个单独的配置类,而是将所有实体类的配置信息都些在继承Dbcontext类的里面,出于解耦考虑可以手动将配置信息单独写到配置文件中。 -
特别注意:
1、如果需要运行第二次的反向工程,之前对代码的所有手动修改均会被覆盖。
2、不建议把反向工具当成日常开发工具使用,不建议DBFirs
3、基于以上及使用场景:尽量只是在针对新项目使用已有的数据库的需求背景下使用、同一个项目仅运行一次反向工程。 -
其它:
关于DbFirst、CodeFirst、ModelFirst的区别及定义可以另寻资料。
三、拓展
1、指定表,通过-tables 表1、表2
或者 -table 表A -table 表B
实现
Scaffold-DbContext "server=DESKTOP-E3D2J8F\SQLEXPRESS;Database=EFCore; user id=sa;
Password=123456;TrustServerCertificate=true" Microsoft.EntityFrameworkCore.SqlServer -tables T_Books
2、Context和Model指定的输出文件夹
-ContextDir 文件夹A/文件夹B
-OutputDir 文件夹
- 这里文件夹一般是个相对路径,前缀隐式使用的是当前项目路径。
Scaffold-DbContext "server=DESKTOP-E3D2J8F\SQLEXPRESS;Database=EFCore; user id=sa;
Password=123456;TrustServerCertificate=true" Microsoft.EntityFrameworkCore.SqlServer
-tables T_Books -ContextDir Context -OutputDir Model
3、 -Force 强制覆盖
- 对于已经被引入的实体,再二次引入就会出现如下图报错,可通过强制覆盖解决,但是如果原有的实体类中有Data Annotation配置也会被覆盖。
- 需要根据实际情况选择是否强制覆盖,这里的报错也可以作为一种校验的方式,保证不重复引入同一个实体类。
4、保留数据库名称:-UseDatabaseNames
- 这个我觉得看实际情况,如果你的数据库的命名很规范,就可以使用这个保证代码和数据库命名的一致性。
- 如果你的数据库内命名就是混乱,可以让EFCORE自动帮你转的相对规范一些,就不要使用这个命令。
5、关于表之间的关系:一对多、多对多
如果你的A表与B表在数据库中有关联关系,但是你的一次反向工程只引入A表,那么就会出现告警:EF Core 无法找到某个外键关联主表的实体类,因此跳过了该外键的生成。
-
对于此,规范的解决方案:就是尽量把具有关联关系的表一起引入,如果实际确实不需要使用关联表也可以不用引入。
关于具有多对多关系的两个表,如何生成实体类,可以参考官方文档。
-
关于具有一对多关系的,实体类中会有一个关联属性,这里可以参考EF CORE的一对多如何配置
四、关于如何指定反向工程策略的思考
1、个人建议,在某些场景下,反向工程+手动修改代码应该是最优解。
- 假设我有这样的一个报表需求,我的项目里面都是写报表需要的API接口,那么此时我对于我的项目要使用哪些数据表是不能充分考虑到位的,需要使用到的实体类会随着需求的新增逐渐新增。此时就会需要多次进行反向工程。
- 但是如杨中科老师所说,如果需要运行第二次的反向工程,之前对代码的所有手动修改均会被覆盖,比如DBContext、实体类内的配置配置的细微修改。
2、默认情况下,实体类的配置信息都是默认存放在DBContext中的。
-
所以对于实体类的所有配置要单独抽出来形式配置类。这里不明白的可以参考EF Core配置教程内关于实体类配置部分。
3、对1中的场景,基于EF CORE的特性,可以设计出如下方法完成新项目、旧表的配置。
- 对于每次新增实体类,都使用反向工程命令
- 但是使用完反向工程命令后,DbContext文件都是一个新的,需要对DbContext的代码稍微修正下
- 刚新加的实体类,配置部分将其从DbContext中抽出来,手动将其单独形成类。
- 对所有历史的、新增的实体类再手动以DbSet形式加入到DbContext中。
4、其实不使用反向工程,直接手动添加实体类、DbConetxt也可以,但是手动添加实体类主要还是表关系容易遗漏。
- 所以在1这种场景下,使用反向工程的主要作用就是引入实体类,并获取到EFCORE对实体类配置形成的初版代码(EFCORE对命名得规范之类)、以及表中字段得具体配置信息(主要还是关联关系),再手工略微修改。
- 至于DbContext,相当于就是手动制作了。