Add-Migration 命令使用啥来比较模式?
Posted
技术标签:
【中文标题】Add-Migration 命令使用啥来比较模式?【英文标题】:What does Add-Migration command use to compare schemas?Add-Migration 命令使用什么来比较模式? 【发布时间】:2020-11-13 09:11:38 【问题描述】:我正在重命名几个表和列。由于我不想丢失现有数据,因此我从头开始创建了迁移。
protected override void Up(MigrationBuilder migrationBuilder)
migrationBuilder.RenameTable(name: "Providers", schema: "dbo", newName: "Vendors", newSchema: "dbo");
migrationBuilder.RenameTable(name: "ProviderContacts", schema: "dbo", newName: "VendorContacts", newSchema: "dbo");
migrationBuilder.RenameColumn(name: "ProviderId", table: "AspNetUsers", newName: "VendorId", schema: "dbo");
migrationBuilder.RenameColumn(name: "ProviderId", table: "VendorContacts", newName: "VendorId", schema: "dbo");
migrationBuilder.RenameColumn(name: "ProviderId", table: "Locations", newName: "VendorId", schema: "dbo");
然后我跑了Update-Database
。但这仍然留下了一堆也应该重命名的索引和外键。所以我使用了Add-Migration
命令来更新任何剩余的更改。
但我注意到新迁移似乎正在删除并重新创建我的自定义迁移已重命名的表和列。
migrationBuilder.DropTable(
name: "ProviderContacts");
migrationBuilder.DropTable(
name: "Providers");
migrationBuilder.DropIndex(
name: "IX_Locations_ProviderId",
table: "Locations");
migrationBuilder.DropIndex(
name: "IX_AspNetUsers_ProviderId",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "ProviderId",
table: "Locations");
migrationBuilder.DropColumn(
name: "ProviderId",
table: "AspNetUsers");
我的问题是:Add-Migration
命令比较的是什么?它是在查看数据库还是其他什么?为什么它没有识别出这些表和列已被重命名?
如何让它识别我的自定义迁移中的更改?
【问题讨论】:
【参考方案1】:在 EF Core 中,Update-Database
使用 IMigrationsAssembly
服务来发现编译到程序集中的迁移。它使用另一个服务获取已针对该数据库运行的迁移列表。随着迁移的执行,迁移列表会在表中更新。该过程不知道也不关心您的数据库模型的状态,它不会导致您的数据库模型发生任何变化。该过程旨在针对其他数据库工作,无需了解您的开发环境。
运行Add-Migration
使用IMigrationsAssembly
服务来加载先前已编译到您的程序集中的ModelSnapshot
,以及来自您的上下文OnModelCreating
方法的模型。然后它使用IMigrationsModelDiffer
服务两次来比较两个模型并生成向上和向下迁移例程。然后将新的迁移和ModelSnapshot
序列化回源代码并写入磁盘。这个过程对任何数据库的状态或之前迁移中的操作列表一无所知。
换句话说,Add-Migration
并不关心您是否编写了另一个迁移。它仍然会比较这两个模型。迁移差异并不完美,如果前后表差异太大,则会产生删除和创建步骤。
另一种选择是一次执行一个更改,为每个步骤创建新的迁移。然后将 Up & Down 方法合并到最后一个迁移中。并删除其余部分。
【讨论】:
是的,我现在看到了ApplicationDbContextModelSnapshot
课程。我现在已经删除了我的自定义迁移。不幸的是,如果我编辑迁移,我看不到任何将其应用到快照的方法。
调用Update-Database会执行迁移并保存快照
@Colin:正如您在上面看到的,它没有这样做。我重命名了一个表和列并运行Update-Database
。但是当我创建一个新的迁移时,它会删除并重新创建同一个表和列。所以我看不到它确实更新了快照。
@JonathanWood 你已经完成了 1.Add-migration 2.Edit migration 3.Update-Database (更改数据库并将模型保存为快照,但模型与以前的模型相同,因为代码没有已更改,现在快照和数据库不再匹配) 4.更改代码中的模型 5.Add-migration 正常过程是 1.更改代码中的模型 2.Add-migration 3.编辑迁移以适合您想要做的(重命名而不是删除) 4.Update-Database(模型存储为快照)。所以现在您应该从第二次迁移中删除删除和重新创建语句并再次运行更新数据库。【参考方案2】:
EF 在将您的 C# 模型与数据库模型进行比较时,不会使用您的实际数据库架构或迁移文件的内容。
当您运行 Update-Database Entity Framework 时,会序列化您的 C# 模型并将其存储在 __MigrationHistory 表中。
您已更改 C# 模型中类的名称,然后调用 Add-Migration,因此 EF 会发现模型已更改并创建迁移以进行更改。 如果您不喜欢它进行更改的方式,或者您需要更新数据以使迁移工作,您可以更改它创建的迁移。
因此,通常您不会在更改 C# 类之前创建初始迁移。您应该首先更改 C# 类。然后 Add-Migration
并更改生成的迁移,以便在您调用 Update-Database
时重命名表而不是删除它们
在您目前的情况下,您可以从第二次迁移中删除 drop 和 recreate 语句,因为重命名已经发生
【讨论】:
EF Core 不会将模型存储在数据库中,而是编译成ModelSnapshot
类。
我明白了。我已经描述了 EF6 的工作原理。但机制并不那么重要。关键是它并不关心您是否编写了另一个迁移。它仍然会比较两个模型。因此,在当前情况下,我认为最简单的解决方案是删除不需要的 drop 并重新创建语句,然后 Update-Database 将新模型存储在快照中
@Colin:我确实运行了Update-Database
。我在我的问题中指出了这一点。所以这对我来说就是断开连接的地方。
@JonathanWood 您的问题是您在第一次迁移后调用了 Update-Database。您也需要在第二次迁移后调用 Update-Database,但您必须先删除不需要的 drop 并重新创建语句以上是关于Add-Migration 命令使用啥来比较模式?的主要内容,如果未能解决你的问题,请参考以下文章
我应该用啥来比较 DBNull ?使用 DBNull.Value 或 ToString().IsNullOrEmpty()
ASP.NET Core EF Add-Migration 命令不起作用
ABP-vs2017执行Add-Migration出现的问题