- ASP.NET Core 中的 Razor 页面和 EF Core - 迁移 - 第 4 个教程(共 8 个)Razor Pages with EF Core in ASP.NET Core - Migrations - 4 of 8
- 删除数据库Drop the database
- 创建初始迁移Create an initial migration
- Up 和 Down 方法Up and Down methods
- 迁移历史记录表The migrations history table
- 数据模型快照The data model snapshot
- 删除 EnsureCreatedRemove EnsureCreated
- 在生产环境中应用迁移Applying migrations in production
- 疑难解答Troubleshooting
- 后续步骤Next steps
- 删除数据库Drop the database
- 创建初始迁移并更新 DBCreate an initial migration and update the DB
- 在生产环境中应用迁移Applying migrations in production
- 疑难解答Troubleshooting
ASP.NET Core 中的 Razor 页面和 EF Core - 迁移 - 第 4 个教程(共 8 个)Razor Pages with EF Core in ASP.NET Core - Migrations - 4 of 8
本文内容
作者:Tom Dykstra、Jon P Smith 和 Rick Anderson
Contoso University Web 应用演示了如何使用 EF Core 和 Visual Studio 创建 Razor 页面 Web 应用。若要了解系列教程,请参阅第一个教程。
如果遇到无法解决的问题,请下载已完成的应用,然后对比该代码与按教程所创建的代码。
本教程介绍管理数据模型更改的 EF Core 迁移功能。
开发新应用时,数据模型会频繁更改。每当模型发生更改时,都无法与数据库进行同步。本教程从配置实体框架以创建数据库(如果不存在)开始。数据模型每次发生更改时,必须删除该数据库。下次应用运行时,对 EnsureCreated
的调用将重新创建数据库以匹配新的数据模型。然后 DbInitializer
类将运行以设定新数据库的种子。
这种使 DB 与数据模型保持同步的方法适用于多种情况,但将应用部署到生产环境的情况除外。当应用在生产环境中运行时,应用通常会存储需要保留的数据。每当发生更改(例如添加新列)时,应用都无法在具有测试数据库的环境下启动。EF Core 迁移功能通过启用 EF Core 更新数据库架构而不是创建新数据库来解决此问题。
数据模型更改时,迁移不会删除并重新创建数据库,而是更新架构并保留现有数据。
备注
SQLite 限制
本教程使用 Entity Framework Core 迁移功能(若可行) 。迁移会更新数据库架构,使其与数据模型中的更改相匹配。但迁移只执行数据库引擎支持的更改类型,而 SQLite 的架构更改功能受限。例如,支持添加列,但不支持删除列。如果已创建迁移以删除列,则 ef migrations add
命令将成功,但 ef database update
命令会失败。
要绕开 SQLite 限制,可手动写入迁移代码,在表内容更改时重新生成表。代码将在 Up
和 Down
方法中用于迁移,并且将涉及以下内容:
- 创建新表。
- 将旧表中的数据复制到新表中。
- 放弃旧表。
- 为新表重命名。
本教程不涉及编写此类型的特定于数据库的代码。相反,每当尝试应用迁移失败时,本教程将删除并重新创建数据库。有关更多信息,请参见以下资源:
删除数据库Drop the database
使用 SQL Server 对象资源管理器 (SSOX) 删除数据库或在包管理器控制台 (PMC) 中运行以下命令 :
Drop-Database
- 在命令提示符下运行以下命令以安装 EF CLI:
dotnet tool install --global dotnet-ef
在命令提示符下,导航到项目文件夹。项目文件夹包含 ContosoUniversity.csproj 文件 。
删除 CU.db 文件,或运行以下命令 :
dotnet ef database drop --force
创建初始迁移Create an initial migration
在 PMC 中运行以下命令:
Add-Migration InitialCreate
Update-Database
确保命令提示符位于项目文件夹中,并运行以下命令:
dotnet ef migrations add InitialCreate
dotnet ef database update
Up 和 Down 方法Up and Down methods
EF Core migrations add
命令已生成用于创建数据库的代码。此迁移代码位于 Migrations<timestamp>_InitialCreate.cs 文件中 。InitialCreate
类的 Up
方法创建与数据模型实体集对应的数据库表。Down
方法删除这些表,如下例所示:
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
namespace ContosoUniversity.Migrations
{
public partial class InitialCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Course",
columns: table => new
{
CourseID = table.Column<int>(nullable: false),
Title = table.Column<string>(nullable: true),
Credits = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Course", x => x.CourseID);
});
migrationBuilder.CreateTable(
name: "Student",
columns: table => new
{
ID = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
LastName = table.Column<string>(nullable: true),
FirstMidName = table.Column<string>(nullable: true),
EnrollmentDate = table.Column<DateTime>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Student", x => x.ID);
});
migrationBuilder.CreateTable(
name: "Enrollment",
columns: table => new
{
EnrollmentID = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
CourseID = table.Column<int>(nullable: false),
StudentID = table.Column<int>(nullable: false),
Grade = table.Column<int>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Enrollment", x => x.EnrollmentID);
table.ForeignKey(
name: "FK_Enrollment_Course_CourseID",
column: x => x.CourseID,
principalTable: "Course",
principalColumn: "CourseID",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Enrollment_Student_StudentID",
column: x => x.StudentID,
principalTable: "Student",
principalColumn: "ID",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Enrollment_CourseID",
table: "Enrollment",
column: "CourseID");
migrationBuilder.CreateIndex(
name: "IX_Enrollment_StudentID",
table: "Enrollment",
column: "StudentID");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Enrollment");
migrationBuilder.DropTable(
name: "Course");
migrationBuilder.DropTable(
name: "Student");
}
}
}
前面的代码适用于初始迁移。代码:
- 由
migrations add InitialCreate
命令生成。 - 由
database update
命令执行。 - 为数据库上下文类指定的数据模型创建数据库。
迁移名称参数(本示例中为“InitialCreate”)用于指定文件名。迁移名称可以是任何有效的文件名。最好选择能概括迁移中所执行操作的字词或短语。例如,添加了系表的迁移可称为“AddDepartmentTable”。
迁移历史记录表The migrations history table
- 使用 SSOX 或 SQLite 工具检查数据库。
- 请注意,增加了
EFMigrationsHistory
表。EFMigrationsHistory
表跟踪已应用到数据库的迁移。 - 查看
__EFMigrationsHistory
表中的数据。它显示第一次迁移的行。
数据模型快照The data model snapshot
迁移会在 Migrations/SchoolContextModelSnapshot.cs 中创建当前数据模型的快照 。添加迁移时,EF 会通过将当前数据模型与快照文件进行对比来确定已更改的内容。
由于快照文件跟踪数据模型的状态,因此不能通过删除 <timestamp>_<migrationname>.cs
文件来删除迁移。要返回最近的迁移,必须使用 migrations remove
命令。该命令删除迁移并确保正确重置快照。有关详细信息,请参阅 dotnet ef migrations remove。
删除 EnsureCreatedRemove EnsureCreated
本系列教程从使用 EnsureCreated
开始。EnsureCreated
不创建迁移历史记录表,因此不能与迁移一起使用。它专门用于在频繁删除并重新创建 DB 的情况下进行测试或快速制作原型。
从这个角度来看,教程将使用迁移。
在 Data/DBInitializer.cs 中,注释掉以下行 :
context.Database.EnsureCreated();
运行应用并验证数据库是否已设定种子。
在生产环境中应用迁移Applying migrations in production
不建议生产应用在应用程序启动时调用 Database.Migrate 。Migrate
不应从部署到服务器场的应用中调用。如果应用横向扩展到多个服务器实例,则很难确保多个服务器不会发生数据库架构更新,或者这些更新不会与读/写访问冲突。
应在部署过程中以受控的方式执行数据库迁移。生产数据库迁移方法包括:
- 使用迁移创建 SQL 脚本,并在部署过程中使用 SQL 脚本。
- 在受控的环境中运行
dotnet ef database update
。
疑难解答Troubleshooting
如果应用使用 SQL Server LocalDB 并显示以下异常:
SqlException: Cannot open database "ContosoUniversity" requested by the login.
The login failed.
Login failed for user 'user name'.
解决方案可能是在命令提示符下运行 dotnet ef database update
。
其他资源Additional resources
后续步骤Next steps
下一个教程将生成数据模型,并添加实体属性和新实体。
本教程使用 EF Core 迁移功能管理数据模型更改。
如果遇到无法解决的问题,请下载已完成应用。
开发新应用时,数据模型会频繁更改。每当模型发生更改时,都无法与数据库进行同步。本教程首先配置 Entity Framework 以创建数据库(如果不存在)。每当数据模型发生更改时:
- DB 都会被删除。
- EF 都会创建一个新数据库来匹配该模型。
- 应用使用测试数据为 DB 设定种子。
这种使 DB 与数据模型保持同步的方法适用于多种情况,但将应用部署到生产环境的情况除外。当应用在生产环境中运行时,应用通常会存储需要保留的数据。每当发生更改(例如添加新列)时,应用都无法在具有测试 DB 的环境下启动。EF Core 迁移功能可通过使 EF Core 更新 DB 架构而不是创建新 DB 来解决此问题。
数据模型发生更改时,迁移将更新架构并保留现有数据,而无需删除或重新创建 DB。
删除数据库Drop the database
使用 SQL Server 对象资源管理器 (SSOX) 或 database drop
命令 :
在“包管理器控制台”(PMC) 中运行以下命令 :
Drop-Database
从 PMC 运行 Get-Help about_EntityFrameworkCore
,获取帮助信息。
打开命令窗口并导航到项目文件夹。项目文件夹包含 Startup.cs 文件 。
在命令窗口中输入以下内容:
dotnet ef database drop
创建初始迁移并更新 DBCreate an initial migration and update the DB
生成项目并创建第一个迁移。
Add-Migration InitialCreate
Update-Database
dotnet ef migrations add InitialCreate
dotnet ef database update
了解 Up 和 Down 方法Examine the Up and Down methods
EF Core migrations add
命令已生成用于创建 DB 的代码。此迁移代码位于 Migrations<timestamp>_InitialCreate.cs 文件中 。InitialCreate
类的 Up
的方法创建与数据模型实体集相对应的 DB 表。Down
方法删除这些表,如下例所示:
public partial class InitialCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Course",
columns: table => new
{
CourseID = table.Column<int>(nullable: false),
Title = table.Column<string>(nullable: true),
Credits = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Course", x => x.CourseID);
});
migrationBuilder.CreateTable(
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Enrollment");
migrationBuilder.DropTable(
name: "Course");
migrationBuilder.DropTable(
name: "Student");
}
}
迁移调用 Up
方法为迁移实现数据模型更改。输入用于回退更新的命令时,迁移调用 Down
方法。
前面的代码适用于初始迁移。该代码是运行 migrations add InitialCreate
命令时创建的。迁移名称参数(本示例中为“InitialCreate”)用于指定文件名。迁移名称可以是任何有效的文件名。最好选择能概括迁移中所执行操作的字词或短语。例如,添加了系表的迁移可称为“AddDepartmentTable”。
如果创建了初始迁移并且存在 DB:
- 会生成 DB 创建代码。
- DB 创建代码不需要运行,因为 DB 已与数据模型相匹配。即使 DB 创建代码运行也不会做出任何更改,因为 DB 已与数据模型相匹配。
如果将应用部署到新环境,则必须运行 DB 创建代码才能创建 DB。
先前删除了 DB,因此已不存在,所以迁移会创建新的 DB。
数据模型快照The data model snapshot
迁移在 Migrations/SchoolContextModelSnapshot.cs 中创建当前数据库架构的快照 。添加迁移时,EF 会通过将数据模型与快照文件进行对比来确定已更改的内容。
若要删除迁移,请使用以下命令:
Remove-Migration
dotnet ef migrations remove
有关详细信息,请参阅 dotnet ef migrations remove。
删除迁移命令会删除迁移并确保正确重置快照。
删除 EnsureCreated 并测试应用Remove EnsureCreated and test the app
早期开发使用了 EnsureCreated
。本教程将使用迁移。EnsureCreated
具有以下限制:
- 绕过迁移并创建 DB 和架构。
- 不会创建迁移表。
- 不能与迁移一起使用 。
- 专门用于在频繁删除并重新创建 DB 的情况下进行测试或快速制作原型。
删除 EnsureCreated
:
context.Database.EnsureCreated();
运行应用并验证 DB 设定为种子。
检查数据库Inspect the database
使用 SQL Server 对象资源管理器检查 DB 。请注意,增加了 EFMigrationsHistory
表。EFMigrationsHistory
表跟踪已应用到 DB 的迁移。查看 __EFMigrationsHistory
表中的数据,其中显示对应初始迁移的一行数据。上面的 CLI 输出示例中最后部分的日志显示了创建此行的 INSERT 语句。
运行应用并验证一切正常运行。
在生产环境中应用迁移Applying migrations in production
不建议生产应用在应用程序启动时调用 Database.Migrate 。不应从服务器场中的应用调用 Migrate
。例如,已将应用在云中部署为横向扩展(运行应用的多个示例)的情况。
应在部署过程中以受控的方式执行数据库迁移。生产数据库迁移方法包括:
- 使用迁移创建 SQL 脚本,并在部署过程中使用 SQL 脚本。
- 在受控的环境中运行
dotnet ef database update
。
EF Core 使用 __MigrationsHistory
表查看是否需要运行任何迁移。如果 DB 已是最新,则无需运行迁移。
疑难解答Troubleshooting
下载已完成应用。
应用会生成以下异常:
SqlException: Cannot open database "ContosoUniversity" requested by the login.
The login failed.
Login failed for user 'user name'.
解决方案:运行 dotnet ef database update