Azure devops 调用SqlCmd for migrations脚本在将引入的列上失败

Azure devops 调用SqlCmd for migrations脚本在将引入的列上失败,azure-devops,entity-framework-core,azure-sql-database,entity-framework-migrations,Azure Devops,Entity Framework Core,Azure Sql Database,Entity Framework Migrations,我试图将数据库迁移放入我的发布管道定义中,大致遵循-dotnet ef migrations脚本--idempotent作为构建管道的一部分,然后执行一个Invoke SqlCmd任务,作为发布管道的一部分指向生成的脚本 然而,我在那篇博文中做了一个改变,这可能很重要:为了避免部署了一半迁移的损坏状态,我将整个脚本包装在一个事务中,以便有效地执行迁移 SET XACT_ABORT ON BEGIN TRANSACTION -- output of dotnet ef migrations s

我试图将数据库迁移放入我的发布管道定义中,大致遵循-
dotnet ef migrations脚本--idempotent
作为构建管道的一部分,然后执行一个
Invoke SqlCmd
任务,作为发布管道的一部分指向生成的脚本

然而,我在那篇博文中做了一个改变,这可能很重要:为了避免部署了一半迁移的损坏状态,我将整个脚本包装在一个事务中,以便有效地执行迁移

SET XACT_ABORT ON

BEGIN TRANSACTION

-- output of dotnet ef migrations script --idempotent

COMMIT
但是,当我尝试将脚本作为发布管道的一部分运行时,在Azure SQL数据库任务中,就像他在博客文章中使用的任务一样,它会失败,例如,对架构中尚不存在的内容的引用,但会在脚本到达包含引用的部分时失败

例如,考虑以下迁移脚本:

SET XACT_ABORT ON

BEGIN TRANSACTION
-- in the actual script, each of these statements are, individually, wrapped in an IF that
-- checks whether the migration has been run before or not, similar to the first one in
-- this listing

IF NOT EXISTS (SELECT * FROM [__EFMigrationsHistory] WHERE MigrationId = 'AddColumnBar')
BEGIN
    ALTER TABLE Foo ADD Bar int NULL
END

GO

UPDATE Foo SET Bar = 3
GO

ALTER TABLE Foo ALTER COLUMN Bar int NOT NULL
GO

COMMIT
按照博文中的建议,使用Invoke SqlCmd执行此脚本会产生一个错误,指出
Foo
列不存在

我如何告诉Invoke-SqlCmd我知道它没有,但在需要时它会这样做?如果这不可能,那么为Azure DevOps修复此部署流的最佳方法是什么


其他信息:

以下是发布管道中SQL步骤的完整日志:

2019-01-09T14:57:52.7983184Z ##[section]Starting: Apply EF Migrations
2019-01-09T14:57:52.7989024Z ==============================================================================
2019-01-09T14:57:52.7989311Z Task         : Azure SQL Database Deployment
2019-01-09T14:57:52.7989427Z Description  : Deploy Azure SQL DB using DACPAC or run scripts using SQLCMD
2019-01-09T14:57:52.7989514Z Version      : 1.2.9
2019-01-09T14:57:52.7989608Z Author       : Microsoft Corporation
2019-01-09T14:57:52.7989703Z Help         : [More Information](https://aka.ms/sqlazuredeployreadme)
2019-01-09T14:57:52.7989823Z ==============================================================================
2019-01-09T14:57:58.8012013Z Sql file: D:\a\r1\a\_db-migrations\migrations-with-transaction.sql
2019-01-09T14:57:58.8189093Z Invoke-Sqlcmd -ServerInstance "***" -Database "***" -Username "***"  -Password ******  -Inputfile "D:\a\r1\a\db-migrations\migrations-with-transaction.sql"  -ConnectionTimeout 120
2019-01-09T14:58:04.3140758Z ##[error]Invalid column name 'Group_DocumentTagGroupId'.Check out how to troubleshoot failures at https://aka.ms/sqlazuredeployreadme#troubleshooting-
2019-01-09T14:58:04.3480044Z ##[section]Finishing: Apply EF Migrations
以下是脚本中对
Group\u DocumentTagGroupId
的所有引用:

-- starting around line 1740
IF NOT EXISTS(SELECT * FROM [__EFMigrationsHistory] WHERE [MigrationId] = N'20181026122735_AddEntityForDocumentTagGroup')
BEGIN
    ALTER TABLE [DocumentTags] ADD [Group_DocumentTagGroupId] bigint NOT NULL DEFAULT 0;
END;

GO

-- about 20 lines with no mention of the column (but several GO statements)

IF NOT EXISTS(SELECT * FROM [__EFMigrationsHistory] WHERE [MigrationId] = N'20181026122735_AddEntityForDocumentTagGroup')
BEGIN
    EXEC('
                    UPDATE [dbo].[DocumentTags] SET [Group_DocumentTagGroupId] = (SELECT TOP 1 [DocumentTagGroupId] FROM [dbo].[DocumentTagGroups] g WHERE g.[Name] = [Type])')
END;

GO

IF NOT EXISTS(SELECT * FROM [__EFMigrationsHistory] WHERE [MigrationId] = N'20181026122735_AddEntityForDocumentTagGroup')
BEGIN
    CREATE INDEX [IX_DocumentTags_Group_DocumentTagGroupId] ON [DocumentTags] ([Group_DocumentTagGroupId]);
END;

GO

IF NOT EXISTS(SELECT * FROM [__EFMigrationsHistory] WHERE [MigrationId] = N'20181026122735_AddEntityForDocumentTagGroup')
BEGIN
    ALTER TABLE [DocumentTags] ADD CONSTRAINT [FK_DocumentTags_DocumentTagGroups_Group_DocumentTagGroupId] FOREIGN KEY ([Group_DocumentTagGroupId]) REFERENCES [DocumentTagGroups] ([DocumentTagGroupId]) ON DELETE CASCADE;
END;

GO
发送用于调用SqlCmd的SQL文件是使用以下PowerShell脚本生成的

$migrationsWithoutTransaction = "./Migrations/scripts/migrations-without-transaction.sql"
dotnet ef migrations script --configuration Release --idempotent --output $migrationsWithoutTransaction

$migrationsWithTransaction = "./Migrations/scripts/migrations-with-transaction.sql"
Get-Content "./Migrations/begin-transaction.sql" | Out-File -Encoding Utf8 $migrationsWithTransaction
Get-Content $migrationsWithoutTransaction | Out-File -Encoding Utf8 -Append $migrationsWithTransaction
Get-Content "./Migrations/commit-transaction.sql" | Out-File -Encoding Utf8 -Append  $migrationsWithTransaction
其中,这是辅助sql脚本的内容:

-- Migrations/begin-transaction.sql
SET XACT_ABORT ON

BEGIN TRANSACTION

-- Migrations/end-transaction.sql
COMMIT

问题是由
BEGIN-TRAN
引起的。这是一个脚本,不是SQL语句或一批语句。像
GO
这样的脚本命令被工具本身(SSMS、sqlcmd或Invoke sqlcmd)理解为批定界符。它可能包含在SQL批处理中不起作用的其他内容,如数据库的脚本变量、名称等。这意味着服务器现在会看到一个批处理,其中包含
BEGIN TRANSACTION
,以及引用不存在的列的其他语句,并且无法解析该批处理。事实上,通过盲目添加该事务并
将XACT_ABORT设置为ON
可能会阻止脚本从失败中恢复。如果您打算修改脚本,请先研究它以了解它的作用。如果确实要使用事务,请在更改后添加一个
GO
。针对本地SQL Server安装测试脚本我确实在本地SQL Server安装上测试了脚本,包括事务包装(尽管它是从SSMS运行的,而不是通过调用SqlCmd)。它做了我希望它做的事情,包括如果我在末尾添加了
THROW
语句,则恢复事务中的所有内容。该脚本是由
dotnet ef migrate script
生成的,尽管我今天可以研究该脚本以了解该版本的功能,但下一个版本将(另外)有所不同。在顶部和底部之外的任何位置修改脚本都不是一个选项;我希望尽可能少地干扰它。@PanagiotisKanavos在我看来,问题不在于脚本的执行,而是在执行之前对脚本进行某种验证。该验证得出的结论是,该列不存在,并且出错,而没有给脚本的前面部分创建该列的机会。您没有发布实际脚本、实际错误或发生此错误的行。没有这些是不可能的。现在我正在创建一个新的webapi项目,只是想看看脚本是什么样子。这与任何工具限制无关,脚本就是这样工作的。如果有语法错误,您需要查看它发生在哪里以及原因。脚本语言与管理工作室中使用的脚本语言相同