通过使用Add-Migration和Update-database两个命令,我们对于EF Core的数据库迁移有了基本的了解,本节将深入介绍数据库迁移的更多用法。
一、 数据库迁移原理
数据库迁移的使用看似很简单,但是内部实现非常复杂,只有了解它的内部实现原理,我们才能更好地使用它。我们查看一下之前演示项目的数据库迁移代码结构,如图4-14所示。数据库迁移代码结构Migrations文件夹下的内容都是数据库迁移生成的代码,这些代码记录了对数据库的修改操作 ,每一个文件代表一次对数据库的修改操作;另一部分是ModelSnapshot.cs文件,开头当前状态的快照。 我们注意到,每次执行Add-Migration 之后,Migrations文件夹下都会生成两个文件,个文件的名字为“数字_迁移名字cs”,另一个文件的名字为“数字_迁移名字.Designer.cs”,我们把每一次执行Add-Migration 称作一次“迁移”。这些以数字开头的一组文件就对应了一次迁移,这些迁移开头的数字就是迁移的版本号,这些版本号是递增的,因此我们根据版本号对其进行排序就能得知数据库迁移的历史。使用迁移脚本,我们可以对当前连接的数据库执行版本号更高的迁移,这个操作叫作“向上迁移”,我们也可以执行把数据库回退到旧版本的迁移,这个操作叫“向下迁移”。假设项目中依次有版本号为1001、1002、1003、1004、1005的5个迁移,而当前连接的数据库已经完成的迁移版本号是1003,那么在当前数据库上执行1004这个脚本以后,就完成了向上迁移;我们也可以把当前的数据库回退到1002这个版本,这样就完成了向下迁移。由于EF Core记录了全部的历史版本信息,因此我们还可以连续迁移,比如可以在当前迁移版本号为1003的数据库上向下迁移到1002、再向下迁移到1001。正因为如此,除非有特殊需要,否则我们不要删除 Migrations 文件夹下的代码。接下来,再详细看看每一组迁移中两个文件的作用。以AddAuthor 为例,20201111223317AddAuthor.cs 中记录的是和具体数据库无关的抽象模型,而20201111223317_AddAuthor.Designer.cs 记录的是和具体数据库相关的代码。20201111223317_AddAuthor.cs 文件的主要内容如下边所示。20201111223317_AddAuthor.cs 文件的主要内容
publicpartialclassAddAuthor : Migration
{
protectedoverridevoidUp(MigrationBuildermigrationBuilder)
{
migrationBuilder.CreateTable (name:"Authors",columns:table=>new
{Id=table.Column<Guid>(nullable:false),
Name=table.Column<string>{nullable:true)},
constraints: table=>table.PrimaryKey("PK_Authors", x=>x.Id));
}
protectedoverridevoidDown(MigrationBuildermigrationBuilder)
{
migrationBuilder. DropTable (name: "Authors");
}
AddAuthor类中包含Up和Down两个方法,Up方法中定义的是向上迁移的代码、也就是把上一个版本的数据库迁移到这个版本要执行的代码,而Down方法中定义的则是向下迁移的代码,也就是把这个版本的数据库迁移回上一个旧版本的代码。这些代码都是由迁移工具生成的,一般不需要编写或者修改这些代码,但是为了研究EF Core的原理,我们还是有必要看量它的大概逻辑。并且定义了和实体可以看到,Up方法中,我们调用CreateTable方法创建了 Authors表,类中对应的列,而Down方法中则调用DropTable 方法把 Authors 表删除。当我们在上一个版本的数据库中执行这个迁移脚本的时候,Up方法被执行,因此Authors 表被创建:当我们需要回退到上一个版本的数据库的时候,Down方法就会被执行,因此 Authors表被删除。因为Up方法是Down方法的“撤销操作”,所以这两个方法的代码需要实现完全相反的操作,也就是Down方法中的代码应该恰好把Up方法中对数据库的操作完全撤销,既不缺失一些操作,也不多出额外的操作。20201111223317_AddAuthor.Designer.cs文件中定义的也是和20201111223317 AddAuthor.cs中相同的AddAuthor类,它们两个通过部分类的语法各自组成AddAuthor类的一部分。我们看一下20201111223317_AddAuthor.Designer.cs文件,其主干内容如下图所示。Designer.cs文件的主干内容
[DbContext(typeof(TestDbContext))]
[Migration("20201111223317_AddAuthor")]
partialclassAddAuthort{}
AddAuthor 类上添加的[DbContext(typeof(TestDbContext))]表示这个迁移脚本应用于哪一个上下文,而[Migration("20201111223317_AddAuthor")]代表这个迁移脚本的版本号。 我们再查看一下数据库,会发现数据库中有一个_EFMigrationsHistory表,表中的数据如下边所示。
MigrationId ProductVersion
20201111223217_InitialCreate 3.1.9
20201111223245_AddAuthorMame_ModifyTitle 3.1.9
20201111223317_AddAuthor 3.1.9
EFMigrationsHistory 表中的数据 可以看到,EFMigrationsHistory表中记录的就是当前数据库曾经应用过的迁移脚本,是按照顺序排列的,最后一条数据就是数据库最后一次应用的迁移版本号。EF Core就是基于这张表得知当前连接的数据库的迁移版本号的,因此除非有特殊需要,否则不要修改这张表及其数据。 由于数据库迁移工具需要调用代码编译后的DLL文件去执行数据库迁移逻辑,因此在运行数据库迁移命令的时候,迁移工具会先尝试构建项目,如果项目构建失败,则迁移工作不能继续执行。如果项目代码中有语法错误等会导致构建失败的代码,在执行Add-Migration 等命令的时候,迁移工具就会提示“Build failed”的错误信息。
二、其他数据库迁移命令
除了Add-migration. Update database这两个常用命令之外,EF Core还提供了其他-些数据库迁移命命令。些命令社被使用的机会相对来讲比较少,这里只介绍常用的功能。
1. Update--database其他参数
我们可以用Updale dlabase xx把数据库回滚到xxx迁移脚本之后的状态。注意,这个命令只把当前连接的数据库进行回滚,因此迁移脚本仍然存在。
2.删除迁移脚本
可以用Removemgration命令删除最后一次的迁移脚本。
3.生成迁移脚本
我们可以用Update-database命令执行迁移脚木来自动修改数据库,但是这种方式只适合在开发环境下使用,而不能用于生产环境。因为基于安全考虑,很多公司要求对生产环境数据库的操作必须要经过审计,而EF Core的迁移代码是-个二进制的程序,很难满足审计的要求:而且大部分公司的开发环境并不能直接连接生产环境数据库。
EF Core 中提供了Script-Migration 命令米根据迁移代码生成SQL脚本,比如在[程序包管理器控制台]中输入Script-Migration 并执行,一个包含完整的数据库操作脚本的SQL文件就会被创建和打开。这个脚本可以被提交给相关人员审计,然后在生产数据库中执行。
如果生产数据库已经处于某个迁移版本的状态了,那么我们可以生成这个版本到某个新版本的SQL脚本。比如当前数据库的前一版本是D,通过如下命令可以生成版本D到版本F的SQL脚本: Script-Migration D F.
在EF Core中我们还可以使用context.Database Migrate(代码来对程序当前连接的数据库进行迁移。这种方式是直接在代码中完成数据库迁移,很多公司的安全审计要求提供的是明文的SQL语句,因此我们需要根据公司安全审计要求决定是采用生成迁移sQL脚本的方式,还是通过迁移程序的方式执行数据库迁移。