codecamp

ASP.NET Core 中的 Razor 页面和 EF Core - 迁移

Contoso University Web 应用演示了如何使用 EF Core 和 Visual Studio 创建 Razor 页面 Web 应用。若要了解系列教程,请参阅第一个教程

本教程使用 EF Core 迁移功能管理数据模型更改。

如果遇到无法解决的问题,请下载已完成应用

开发新应用时,数据模型会频繁更改。 每当模型发生更改时,都无法与数据库进行同步。 本教程首先配置 Entity Framework 以创建数据库(如果不存在)。 每当数据模型发生更改时:

  • DB 都会被删除。
  • EF 都会创建一个新数据库来匹配该模型。
  • 应用使用测试数据为 DB 设定种子。

这种使 DB 与数据模型保持同步的方法适用于多种情况,但将应用部署到生产环境的情况除外。 当应用在生产环境中运行时,应用通常会存储需要保留的数据。 每当发生更改(例如添加新列)时,应用都无法在具有测试 DB 的环境下启动。 EF Core 迁移功能可通过使 EF Core 更新 DB 架构而不是创建新 DB 来解决此问题。

数据模型发生更改时,迁移将更新架构并保留现有数据,而无需删除或重新创建 DB。

删除数据库

使用 SQL Server 对象资源管理器 (SSOX) 或 database drop 命令:

在“包管理器控制台”(PMC) 中运行以下命令:

PMC
Drop-Database

从 PMC 运行 Get-Help about_EntityFrameworkCore,获取帮助信息。

创建初始迁移并更新 DB

生成项目并创建第一个迁移。

PMC
Add-Migration InitialCreate
Update-Database

了解 Up 和 Down 方法

EF Core migrations add 命令已生成用于创建 DB 的代码。 此迁移代码位于 Migrations<timestamp>_InitialCreate.cs 文件中。 InitialCreate 类的 Up 的方法创建与数据模型实体集相对应的 DB 表。 Down 方法删除这些表,如下例所示:

C#

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。

数据模型快照

迁移在 Migrations/SchoolContextModelSnapshot.cs 中创建当前数据库架构的快照。 添加迁移时,EF 会通过将数据模型与快照文件进行对比来确定已更改的内容。

若要删除迁移,请使用以下命令:

Remove-Migration

删除迁移命令会删除迁移并确保正确重置快照。

删除 EnsureCreated 并测试应用

早期开发使用了 EnsureCreated。 本教程将使用迁移。 EnsureCreated 具有以下限制:

  • 绕过迁移并创建 DB 和架构。
  • 不会创建迁移表。
  • 不能与迁移一起使用。
  • 专门用于在频繁删除并重新创建 DB 的情况下进行测试或快速制作原型。

删除 EnsureCreated:

C#

context.Database.EnsureCreated();

运行应用并验证 DB 设定为种子。

检查数据库

使用 SQL Server 对象资源管理器检查 DB。 请注意,增加了 __EFMigrationsHistory 表。 __EFMigrationsHistory 表跟踪已应用到 DB 的迁移。 查看 __EFMigrationsHistory 表中的数据,其中显示对应初始迁移的一行数据。 上面的 CLI 输出示例中最后部分的日志显示了创建此行的 INSERT 语句。

运行应用并验证一切正常运行。

在生产环境中应用迁移

不建议生产应用在应用程序启动时调用 Database.Migrate。 不应从服务器场中的应用调用 Migrate。 例如,已将应用在云中部署为横向扩展(运行应用的多个示例)的情况。

应在部署过程中以受控的方式执行数据库迁移。 生产数据库迁移方法包括:

  • 使用迁移创建 SQL 脚本,并在部署过程中使用 SQL 脚本。
  • 在受控的环境中运行 dotnet ef database update。

EF Core 使用 __MigrationsHistory 表查看是否需要运行任何迁移。 如果 DB 已是最新,则无需运行迁移。

疑难解答

下载已完成应用

应用会生成以下异常:

text

SqlException: Cannot open database "ContosoUniversity" requested by the login.
The login failed.
Login failed for user 'user name'.

解决方案:运行 dotnet ef database update


ASP.NET Core 中的 Razor 页面和 EF Core - 排序、筛选、分页
ASP.NET Core 中的 Razor 页面和 EF Core - 数据模型
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }