管理迁移

当模型发生更改时,将在正常开发过程中添加和删除迁移,并将迁移文件签入项目的源代码管理。 若要管理迁移,必须先安装 EF Core 命令行工具

提示

如果 DbContext 与启动项目位于不同程序集中,可以在包管理器控制台工具.NET Core CLI 工具中显式指定目标和启动项目。

添加迁移

更改模型后,可以为该更改添加迁移:

dotnet ef migrations add AddBlogCreatedTimestamp

迁移名称的用途与版本控制系统中的提交消息类似。 例如,如果更改是指 Blog 实体上的新 CreatedTimestamp 属性,你可以选择诸如 AddBlogCreatedTimestamp 之类的名称。

Migrations目录下的项目添加以下三个文件:

  • XXXXXXXXXXXXXX_AddCreatedTimestamp.cs - 主迁移文件。 包含应用迁移所需的操作(在 Up 中)和还原迁移所需的操作(在 Down 中)。
  • XXXXXXXXXXXXXX_AddCreatedTimestamp.Designer.cs - 迁移元数据文件。 包含 EF 所用的信息。
  • MyContextModelSnapshot.cs--当前模型的快照。 用于确定添加下一迁移时的更改内容。

文件名中的时间戳有助于保持文件按时间顺序排列,以便你可以查看更改进展。

命名空间

可以手动移动 Migrations 文件并更改其命名空间。 创建的新迁移属于上个迁移的同级。 或者,可以在生成时指定目录,如下所示:

dotnet ef migrations add InitialCreate --output-dir Your/Directory

注意

还可以使用 --namespace 独立于目录更改命名空间。

自定义迁移代码

虽然 EF Core 通常会创建准确的迁移,但你应始终查看代码,确保它与所需的更改相对应;在某些情况下,甚至有必要这样做。

列重命名

需要自定义迁移的一个重要示例就是重命名属性时。 例如,如果你将属性从 Name 重命名为 FullName,EF Core 将生成以下迁移:

migrationBuilder.DropColumn(
    name: "Name",
    table: "Customers");

migrationBuilder.AddColumn<string>(
    name: "FullName",
    table: "Customers",
    nullable: true);

EF Core 通常无法知道何时要删除列并创建新列(两个不同的更改),何时应对列重命名。 如果按原样应用上述迁移,你的所有客户名称都将丢失。 若要重命名列,请将上面生成的迁移替换为以下内容:

migrationBuilder.RenameColumn(
    name: "Name",
    table: "Customers",
    newName: "FullName");

提示

当某个操作可能会导致数据丢失(例如删除某列),搭建迁移基架过程将对此发出警告。 如果看到此警告,务必检查迁移代码的准确性。

添加原始 SQL

虽然可以通过内置 API 重命名列,但在许多情况下这是不可能的。 例如,我们可能想用一个新的 FullName 属性替换现有的 FirstNameLastName 属性。 EF Core 生成的迁移如下所示:

migrationBuilder.DropColumn(
    name: "FirstName",
    table: "Customer");

migrationBuilder.DropColumn(
    name: "LastName",
    table: "Customer");

migrationBuilder.AddColumn<string>(
    name: "FullName",
    table: "Customer",
    nullable: true);

如前所述,这会导致不必要的数据丢失。 为了从旧列传输数据,我们重新排列迁移并引入原始 SQL 操作,如下所示:

migrationBuilder.AddColumn<string>(
    name: "FullName",
    table: "Customer",
    nullable: true);

migrationBuilder.Sql(
@"
    UPDATE Customer
    SET FullName = FirstName + ' ' + LastName;
");

migrationBuilder.DropColumn(
    name: "FirstName",
    table: "Customer");

migrationBuilder.DropColumn(
    name: "LastName",
    table: "Customer");

通过原始 SQL 进行任意更改

原始 SQL 还可用于管理 EF Core 不知道的数据库对象。 为此,请在不进行任何模型更改的情况下添加迁移;系统将生成一个空迁移,然后你可以使用原始 SQL 操作填充该迁移。

例如,以下迁移将创建一个 SQL Server 存储过程:

migrationBuilder.Sql(
@"
    EXEC ('CREATE PROCEDURE getFullName
        @LastName nvarchar(50),
        @FirstName nvarchar(50)
    AS
        RETURN @LastName + @FirstName;')");

提示

当某个语句必须是 SQL 批处理中的第一个或唯一一个语句时,请使用 EXEC。 它还可以用来解决幂等迁移脚本中的分析程序错误,当表中当前不存在引用的列时,可能会发生此类错误。

这可用于管理数据库的方方面面,包括:

  • 存储过程
  • 全文搜索
  • 函数
  • 触发器
  • 视图

在大多数情况下,EF Core 会在应用迁移时自动将每个迁移包装在其各自的事务中。 遗憾的是,在某些数据库中,某些迁移操作无法在事务中执行;对于这类情况,你可以通过将 suppressTransaction: true 传递给 migrationBuilder.Sql,选择不使用事务。

删除迁移

有时,你可能在添加迁移后意识到需要在应用迁移前对 EF Core 模型作出其他更改。 要删除上个迁移,请使用如下命令。

dotnet ef migrations remove

删除迁移后,可对模型作出其他更改,然后再次添加迁移。

警告

避免删除已应用于生产数据库的任何迁移。 这样做意味着你将无法从数据库还原这些迁移,并且可能会破坏后续迁移所做的假设。

列出迁移

你可以列出现有的所有迁移,如下所示:

dotnet ef migrations list

检查挂起的模型更改

注意

EF Core 8.0 中添加了此功能。

有时,你可能想要检查自上次迁移以来是否进行了任何模型更改。 这可以帮助你了解你或团队成员是否忘记添加迁移。 执行此操作的一种方法是使用此命令。

dotnet ef migrations has-pending-model-changes

还可以使用 context.Database.HasPendingModelChanges() 以编程方式执行此检查。 这可用于编写会在忘记添加迁移时失败的单元测试。

重置所有迁移

在某些极端情况下,可能需要删除所有迁移并重新开始。 这可以通过删除 Migrations 文件夹并删除数据库来轻松完成;此时,你可以创建一个新的初始迁移,其中将包含当前的整个架构。

你还可以重置所有迁移并创建单个迁移,而不会丢失数据。 此操作有时称为“更正”,涉及一些手动操作:

  1. 备份数据库,以防出现问题。
  2. 在数据库中,删除迁移历史记录表中的所有行(例如,SQL Server 上的 DELETE FROM [__EFMigrationsHistory])。
  3. 删除 Migrations 文件夹。
  4. 创建新迁移并为其生成 SQL 脚本 (dotnet ef migrations script)。
  5. 在迁移历史记录中插入一行,以记录第一个迁移已经应用,因为表已经存在。 插入 SQL 是上面生成的 SQL 脚本中的最后一项操作,如下所示(不要忘记更新值):
INSERT INTO [__EFMigrationsHistory] ([MIGRATIONID], [PRODUCTVERSION])
VALUES (N'<full_migration_timestamp_and_name>', N'<EF_version>');

警告

删除 Migrations 文件夹后,所有自定义迁移代码都将丢失。 若要保留任何自定义项,必须手动将其应用到新的初始迁移。

其他资源