《Entity Framework Core in Action》--- 读书随记(6)

Part 2 Entity Framework in depth

《Entity Framework Core in Action》
-- SECOND EDITION

Author: JON P SMITH

如果需要电子书的小伙伴,可以留下邮箱,看到了会发送的

9 Handling database migrations

9.2 Understanding the complexities of changing your application’s database

9.2.2 Handling a migration that can lose data

将迁移分为两组是有帮助的: 非破坏性变更或数据丢失破坏性变更。非破坏性变更是指不删除包含有用数据的表或列,而数据丢失破坏性变更则删除这些表或列。因此,如果您不想丢失重要的数据,就需要为数据丢失突破性更改迁移添加额外的复制阶段,以便保留数据

9.3 Part 1: Introducing the three approaches to creating a migration

有三种主要方法可以提供与应用程序的 DbContext 匹配的更新数据库。每种方法都有一个不同的起点

  • Using EF Core’s migration features: 这种方法考虑实体类,EF Core 配置是真理的来源。这种处理迁移的方法是最简单的,但是处理数据丢失等复杂问题需要手工编辑迁移
  • Using SQL scripts to build migrations: 在这种方法中,真相的来源是用于构建/迁移数据库的 SQL 命令。你可以完全控制你的数据库 schema,并且可以包含 EF Core 不能配置的特性,比如列级约束。但是最大的挑战是将 SQL 更改匹配到 EF Core 的内部模型
  • Using EF Core’s reverse-engineering tool: 在这种方法中,数据库是真相的来源。使用所有必需的配置重新创建实体类和应用程序的 DbContext。这种方法主要用于围绕现有数据库构建 EF Core 应用程序

9.4 Creating a migration by using EF Core’s add migration command

因此,当运行 EF Core 的迁移命令时,它会将快照类与当前实体类进行比较,并将应用程序的 DbContext 与其配置代码进行比较。从这些数据中,它可以计算出发生了哪些更改; 然后构建两个包含命令的类,以便将更改添加到数据库中

9.4.1 Requirements before running any EF Core migration command

有两种数据库工具,一种是CLI(dotnet-ef),另一种是VS的PMC。

CLI的安装dotnet tool install --global dotnet-ef

还需要安装解析数据库模型的工具:Microsoft.EntityFrameworkCore.Tools
以及相应的数据库驱动:Microsoft.EntityFrameworkCore.SqlServer

这些工具必须能够创建要迁移的 DbContext 的实例

如果您的启动项目是 ASP.NET Core Web 主机或 NET Core 通用主机,那么这些工具可以使用它来获取在启动类中设置的 DbContext 的实例

如果不使用 ASP.NET Core,则可以添加实现 IDesignTimeDbContextFactory < TContext > 接口的类。此类必须与要迁移的 DbContext 位于同一个项目中

9.4.2 Running the add migration command

我是Linux平台,所以我使用CLI进行迁移dotnet ef migrations add Ch09Migrate

9.4.3 Seeding your database via an EF Core migration

EF Core 的迁移可以包含将添加到数据库的数据,这个过程称为种子数据库。该特性的一个很好的用途是向数据库中添加常量,例如电子商务站点的产品类型和客户类型。我应该说种子数据是可以更改的,所以数据不是常量,但是您只能通过迁移来更改它,因此最好将它用于不会发生(太多)变化的数据

使用方法 HasData 添加数据

如上所示,必须定义主键,即使通常是由数据库生成的,这样就可以通过将外键设置为适当的主键来定义关系。如果更改主键,则删除以前的种子项。此外,如果保留原始主键但更改该条目中的数据,迁移将更新该条目

9.4.4 Handling EF Core migrations with multiple developers

当多个开发人员在一个项目中使用 EF Core 的迁移特性来更新数据库 schema 时,你可能会遇到软件合并,其中一个开发人员的迁移与你的迁移发生冲突。这个部分给你一些关于怎么做的建议

首先,如果您的迁移与刚刚合并到您的软件中的迁移没有冲突,那么您就不应该有源代码控制冲突,因为 EF Core 的迁移被设计为对团队友好的。您可能以稍微不同的顺序应用迁移; 也许您昨天创建了迁移,今天生成了其他人的迁移并应用到主数据库。如果没有合并冲突,这种情况不会引起问题,因为 EF Core 可以处理无序的迁移

您将知道是否存在迁移合并冲突,因为源代码管理系统将在名为 < DbContextClassName > ModelSnapShot 的迁移快照文件中显示冲突。如果发生这种冲突,以下是修复它的推荐方法:

  1. 中止包含与迁移冲突的迁移更改的源代码管理合并
  2. 使用下列任一命令删除您创建的迁移
    • CLI—dotnet ef migrations remove
    • PMC—Remove-Migration
  3. 合并您在步骤1中放弃的传入迁移。合并冲突不应再出现在迁移快照文件中
  4. 使用迁移命令重新创建迁移

迁移冲突解决过程在大多数情况下都可以工作,但是它可能变得复杂。对于可能发生迁移冲突的项目,我的建议是

  • 在创建迁移之前,将 main/production 分支合并到本地版本中
  • 在源代码管理中只有一次迁移,将其合并到主/生产分支中,因为撤消两次迁移是很困难的工作
  • 如果您认为您的迁移可能会影响您的开发团队成员的工作,请告诉他们

9.4.5 Using a custom migration tabl e to allow multiple DbContexts to one database

如果将 EFCore 迁移到数据库,则 EFCore 将创建一个表。EF Core 使用这个表来找出哪些迁移应用到了数据库,这样它就知道应该对正在迁移的数据库应用哪些迁移。默认情况下,该表名为 _EFMigationsHistory,但是可以通过一个名为 MigationsHistoryTable 的选项方法更改名称

修改迁移历史记录表的原因不多,但是跨多个 EF Core DbContext 共享数据库就是其中之一:

  • Saving money by combining databases -- 您正在使用需要帐户数据库的单个用户帐户构建 ASP.NET Core 应用程序。应用程序的 DbContext 也需要一个数据库。通过在应用程序的 DbContext 上使用自定义迁移表,将允许两个上下文使用相同的数据库
  • Using a separate DbContext for each business group -- 我想让这个项目更容易扩展,因为它变得更大。因此,我有两个单独的 DbContext: 一个用于图书显示代码,另一个用于订单处理代码

这两个示例都可以工作,但是使用 EFCore 的迁移系统需要付出更多的努力。第一个迁移示例ーー通过组合数据库节省资金ーー更容易,因为要组合的两个数据库不共享任何表、视图等。但是因为这两个数据库都使用 EF Core 的迁移系统,所以它们需要一个不同的迁移历史表。ASP.NET Core 的单个用户帐户数据库使用迁移历史表的默认名称,因此需要更改应用程序的 DbContext 名称

对于第二个示例(为每个业务组使用单独的 DbContext) ,需要为每个 DbContext 使用不同的迁移历史表名称,以便每次迁移都是独立的。还应该为每个 DbContext 的迁移类指定单独的目录,可以通过迁移命令中的一个选项来实现这一点。如果在两个 DbContext 中使用相同的迁移名称,该命令将停止类名称的任何冲突

如果愿意,还可以将迁移类放在单独的项目中。您需要告诉迁移命令将迁移放入哪个项目中。然后在设置数据库选项时使用 MigationsAssembly 方法

但是,这个示例还有另一个问题需要处理: 每个 DbContext 都需要访问名为 Books 的表,这将重复对该表的迁移。Books 表是共享的,因为两个 DbContext 都必须能够读取它

您可以使用多种选择来解决这个问题,但是最好的方法是使用 ExcludeFromMigations Fluent API 命令,该命令将阻止该实体类包含在迁移中。在 BookDbContext/OrderDbContext 示例中,可以删除在 OrderDbContext 中对 Book 实体类的迁移

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Book>()
        .ToTable("Books",
            t => t.ExcludeFromMigrations());
}

如果 Book 实体类映射到一个视图,而不是一个表,迁移工具将不会在迁移中包含该视图。对于本例来说,这种方法很好,因为我们希望 BookDbContext 具有读/写访问权,但是 OrderDbContext 应该只具有读访问权

9.5 Editing an EF Core migration to handle complex situations

EF Core 迁移工具功能强大,经过深思熟虑,但它们不能处理所有可能的数据库迁移,比如数据丢失突变。EF Core 团队知道这一点,因此它提供了多种手动更改迁移类的方法。让我们看看标准迁移在没有帮助的情况下无法处理的迁移类型:

  • Data-loss breaking changes,例如将列从一个表移动到新表
  • 添加 EF Core 不创建的 SQL 特性,例如添加用户定义函数、 SQL 存储过程、视图等
  • 将迁移更改为适用于多种数据库类型,例如同时处理 SQLServer 和 PostgreSQL

9.5.1 Adding and removing MigrationBuilder methods inside the migration class

此示例查看如果更改实体类中的属性的名称会发生什么,这将导致data-loss breaking change

这个问题可以通过删除两个命令并在迁移类中使用 MigationBuilder 的 RenameColumn 方法替换它们来解决

标准迁移将此操作视为删除 CustomerId 属性并添加一个名为 UserId 的新属性,这将导致 CustomerId 列中的任何现有值丢失。为了解决这个问题,对生成的标准迁移生成的迁移类进行以下更改:

  • 删除添加新 UserId 列的 AddColumn 命令
  • 删除删除现有 CustomerId 列的 DropColumn 命令
  • 添加 RenameColumn 命令将 CustomerId 列重命名为 UserId

修改的文件是生成的迁移文件,文件名字是CLI命令中输入的迁移名称,后缀是.cs文件,例如
..._InitialMigration.cs

该代码将 Up 迁移从丢失数据的迁移更改为保留旧 CustomerId 列中保存的数据的迁移。由迁移命令创建的迁移类还包含一个 Down 方法。如果已经将 Up 迁移应用到数据库,则此方法将撤消迁移。因此,最佳做法是使用正确的命令编辑 Down 方法来撤消迁移

9.5.2 Adding SQL commands to a migration

首先,更改 User 实体类以删除地址并将新的 Address 实体类链接到 DbContext。然后,使用迁移命令创建一个新的迁移,该命令将警告您,这可能导致数据丢失。此时,您已经准备好编辑迁移了

第二步是使用 MigationBuilder 方法 SQL 添加一系列 SQL 命令,例如migrationBuilder.Sql("ALTER TABLE...”)

通过使用用于每个 SQL 命令的 MigationBuilderSql 方法,将这些 SQL 命令添加到迁移中,并将它们放置在创建地址表之后但设置外键之前。此外,在 SQL 代码运行之后,必须将从 Users 表中删除地址属性的 MigationBuilder 方法移动到其中; 否则,在 SQL 可以复制该数据之前,数据将已经移动

9.5.4 Altering a migration to work for multiple database types

EF Core 迁移是特定于数据库提供程序的ーー也就是说,如果您为 SQL Server 构建迁移,那么它几乎肯定不会适用于 PostgreSQL 数据库。但是,通常不需要对多个数据库类型进行迁移。事实上,我不推荐使用具有相同 EF Core 代码的多个数据库类型,因为数据库类型之间的细微差别可能会暴露您的问题。但是,如果需要支持两种或多种类型的数据库的迁移,建议的方法是为每个数据库提供程序构建单独的迁移

第一步是为每种数据库类型创建特定的 DbContext。最简单的方法是创建主应用程序的 DbContext 并在其他数据库类型中继承它

此时,您可以使用 Add-Immigration 命令为每种数据库类型创建迁移(参见9.4.2节)。重要的一点是,每次迁移都必须位于单独的项目中,以便在创建迁移时,它可以访问与 DbContext 链接的数据库类型的正确迁移类。当您创建数据库选项时,您可以通过使用 MigationsAssembly 方法告诉 EFCore 迁移类可以在哪里找到。下面的代码片段显示了用于向应用程序的数据库提供程序注册应用程序的 DbContext 的 AddDbContext 方法,以及在名为 Database.SqlServer 的项目中对该数据库的迁移

services.AddDbContext<MySqlServerDbContext>(
    options => options.UseSqlServer(connection,
        x => x.MigrationsAssembly("Database.SqlServer")));

9.6 Using SQL scripts to build migrations

暂时跳过

9.7 Using EF Core’s reverse-engineering tool

在某些情况下,您已经有了一个希望通过 EF Core 代码访问的数据库。为此,您需要应用与迁移相反的方法,并允许 EF Core 通过使用现有数据库作为模板来生成实体类和应用程序的 DbContext。这个过程被称为逆向工程数据库。这种方法认为数据库是真相的来源。您可以使用 EF Core 的逆向工程工具(也称为脚手架)来重新创建实体类和应用程序的 DbContext 以及所有必需的配置

9.7.1 Running EF Core’s reverse-engineering command

下面的列表显示了对 BookApp 数据库进行反向工程的脚手架命令。注意,命令在 BookApp ASP.NET Core 项目的目录中运行,数据库连接字符串在该项目的 appsetings.json 文件中

  • CLI -- dotnet ef dbcontext scaffold name=DefaultConnection Microsoft.EntityFrameworkCore.SqlServer
  • PMC—Scaffold-DbContext -Connection name=DefaultConnection -Provider Microsoft.EntityFrameworkCore.SqlServer

9.7.2 Installing and running EF Core Power Tools reverse-engineering command

EF Core Power Tools Visual Studio 扩展是由 Erik Ejlskov Jensen 创建和维护的,他在 GitHub 和 Twitter 上被称为@ErikEJ。该工具使用 EF Core 的逆向工程服务,但提供了一个可视化的前端,使其更容易使用。这个扩展非常有用,因为反向工程代码通常需要很多参数,包括长连接字符串。Erik 的工具还添加了一些特性,比如定制生成代码的模板的能力

9.7.3 Updating your entity classes and DbContext when the database changes

处理数据库更改的一种方法是迁移数据库,然后运行逆向工程工具重新创建实体类和应用程序的 DbContext。这样,你就知道数据库 schema 和 EF Core 的模型是同步的

使用 EF Core 的逆向工程工具可以直接工作,但是您必须记住每次运行的所有设置。EF Core 项目有一个关于 backlog 的特性,它试图保留当前类并只改变已更改的属性和关系。这种特性将会很棒,但是实现起来会很复杂,所以有一段时间没有考虑过这个问题。幸运的是,EF Core Power Tools 扩展是一个不错的替代品

9.8 Part 2: Applying your migrations to a database

到目前为止,您一直在考虑迁移数据库的不同方法。在本节中,您将考虑如何将迁移应用到数据库。您创建迁移的方式会影响您应用它的方式。例如,如果使用 SQL 更改脚本创建迁移,则不能使用 EF Core 的 Migrate 方法应用它们

9.8.1 Calling EF Core’s Database.Migrate method from your main application

在主应用程序启动之前添加了一些调用 context.Database.Migrate 的代码。到目前为止,这种方法是应用迁移最简单的方法,但它有一个很大的局限性: 您不应该同时运行 Migrate 方法的多个实例。如果您的应用程序有多个实例同时运行(这是许多应用程序的特点) ,则不能使用这种方法

9.8.2 Executing EF Core’s Database.Migrate method from a standalone application

dotnet ef database update

posted @ 2022-07-17 17:06  huang1993  阅读(127)  评论(0)    收藏  举报