尽管检查了应用的迁移,EF Core 幂等迁移脚本仍失败

Sup*_*pez 7 sql-server entity-framework entity-framework-core azure-sql-database

我正在使用 EF Core (3.1.15)。在之前的迁移中(也是在 3.1.15 中创建的),引用了一个后来被删除的列。幂等脚本会检查是否在数据库上执行了迁移(确实如此,并且引用仍然显示在表中__EFMigrationsHistory)。但是,由于列不存在,检查没有预期的结果和脚本。

:为什么不存在的列会导致 SQL 脚本的执行失败?

脚本是用创建的

dotnet-ef migrations script -i -o migrations.sql
Run Code Online (Sandbox Code Playgroud)

失败的自动化脚本的相关部分,ReferenceToLedgerId在以后的迁移中删除的列在哪里:

dotnet-ef migrations script -i -o migrations.sql
Run Code Online (Sandbox Code Playgroud)

错误:

消息 207,级别 16,状态 1,第 3
行 列名称“ReferenceToLedgerId”无效

运行以下 SQL 查询时,结果按预期返回:

IF NOT EXISTS(SELECT * FROM [__EFMigrationsHistory] WHERE [MigrationId] = N'20210612052003_CLedger')
BEGIN
    UPDATE LedgerTable SET LedgerId = ReferenceToLedgerId
END;
Run Code Online (Sandbox Code Playgroud)
迁移ID 产品版本
20210612052003_CLedger 3.1.15

该数据库是 Azure SQL 数据库。脚本在本地 SQL 开发数据库上不会失败。从那时起,已经应用了十几次迁移,直到现在脚本才失败。

以下是创建特定脚本的调用:

SELECT * 
FROM [__EFMigrationsHistory] WHERE [MigrationId] = N'20210612052003_CLedger'
Run Code Online (Sandbox Code Playgroud)

我尝试将表名和列名放在方括号中,但这没有什么区别(例如......[ReferenceToLedgerId]该脚本在使用 Azure DevOps 版本时失败SQLCMD,并且在使用 Azure Data Studio 时也失败,两者都访问 Azure SQL 数据库。


额外检查

我更改了脚本来进行快速检查:

migrationBuilder.Sql("UPDATE LedgerTable set LedgerId = ReferenceToLedgerId", true);
Run Code Online (Sandbox Code Playgroud)

我得到以下结果:

从第 1 行开始执行查询 #Before IF #After IF 总执行时间:00:00:00.010

如果我取消注释该UPDATE语句,它会再次失败。因此,我只能得出结论,代码路径按预期工作,但服务器仍然检查该列是否存在。我不熟悉 SQL,无法理解为什么会出现这种情况,或者为什么它只在这一行失败,而在 SQL 脚本的其他行中引用列本身而不会失败。

Dav*_*oft 5

该批处理在每个版本的 SQL Server 上都会失败。例如

use tempdb
go
create table __EFMigrationsHistory(MigrationId nvarchar(200))
create table LedgerTable(LedgerId int)
go
insert into __EFMigrationsHistory(MigrationId) values (N'20210612052003_CLedger')
go
IF NOT EXISTS(SELECT * FROM [__EFMigrationsHistory] WHERE [MigrationId] = N'20210612052003_CLedger')
BEGIN
    UPDATE LedgerTable SET LedgerId = ReferenceToLedgerId
END;
Run Code Online (Sandbox Code Playgroud)

失败与

Msg 207, Level 16, State 1, Line 8
Invalid column name 'ReferenceToLedgerId'.
Run Code Online (Sandbox Code Playgroud)

因为批处理无法解析和编译。在 TSQL 批处理中引用不存在的表或列是不合法的。

您可以通过使用动态 SQL 来解决此问题,这样除非应用迁移,否则不会解析和编译引用不存在列的批处理。

migrationBuilder.Sql("exec('UPDATE LedgerTable set LedgerId = ReferenceToLedgerId')", true);
Run Code Online (Sandbox Code Playgroud)

此处记录了这一点:

提示

当语句必须是 SQL 批处理中的第一个或唯一一个语句时,请使用 EXEC 函数。可能还需要解决幂等迁移脚本中的解析器错误,当表中当前不存在引用的列时可能会发生这些错误。

https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/operations

  • 谢谢,从现在开始我将通过“exec”调用来调用“migrationBuilder.Sql()”。我仍然很困惑为什么这在我的本地数据库和之前的迁移中都没有发生,但我很高兴我有一个解决方案。从现在开始,我还将更加仔细地阅读 Microsoft 文档中的绿色“提示”:https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/operations (2认同)