从 EFCore 开始,就推荐数据库根据实体由工具生成,即开发顺序为

建立实体——建立实体配置——建立DbContext——生成数据库

这样的顺序也称为 Code-First,即实体是比数据库先完成的。但假设我们的项目已经有数据了,这个时候如果要使用 EFCore,就需要从现有数据库生成实体,这个过程叫做 Reverse Engineering(反向工程)。

在 EF 里,也支持先建立数据库再生成实体的开发顺序,称为 Database-First,但这样的开发模式在 EFCore 中已经不被支持,在项目新增实体时,依然推荐使用 Code-First 模式进行开发。

下面介绍一下在 EFCore 里如何进行反向工程。

反向工程是通过 EFCore Tools 完成的,即在终端控制台执行 Scaffold-DbContext 命令,后面只需要接数据库连接字符串和数据库系统即可,以 SQLServer 为例:

Scaffold-DbContext "Data source=localhost;database=Test;Integrated Security=SSPI;" Microsoft.EntityFrameworkCore.SqlServer

执行后,EFCore 就会连接数据库,并根据数据库当前的设计生成实体,包括实体类文件、DbContext类文件等。这个命令是通过 Nuget 包名来指定数据库系统的。

通过反向工程生成的代码,都是按照既有约定的。例如 DbContext 类的名字为 <DatabaseName>Context,而实体类的名字是由表名生成的,例如 T_Books 会生成名为 TBook 的实体类。这个结果往往不是程序员想要的,这个时候可能需要手动修改使其符合规范。

并且反向工程生成的代码是没有创建单独的实体配置类的,而是将所有实体配置都写入 DbContext 中:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.HasAnnotation("Relational:Collation", "Chinese_PRC_CI_AS");

    modelBuilder.Entity<TAuthor>(entity =>
    {
        entity.ToTable("T_Authors");
    });

    modelBuilder.Entity<TBook>(entity =>
    {
        entity.ToTable("T_Books");

        entity.HasIndex(e => e.AuthorId, "IX_T_Books_AuthorId");

        entity.Property(e => e.Title)
            .IsRequired()
            .HasMaxLength(50);
    });

如果实体很多,就可能导致 DbContext 代码混乱,相比独立的实体配置类来说不便管理。并且 EFCore 的反向工程并不具备 Migration 那样的版本更新或回滚功能,每一次 Scaffold-DbContext 命令都会从头开始生成实体,并可能覆盖之前生成的代码。所以反向工程最好只在必要的时候只使用一次,之后应该使用 Code-First 模式开发。