我一直在使用.dacpacs将数据库更新部署到我们的各种环境中。我发现了一个导致发布特定更新失败的场景。
我需要添加一个新表dbo.Supplier
,并向另一个具有引用新表的不可为null的foriegn键的表dbo.PickZone
添加一列。SSDT项目中的模式反映了这一点,为了准备新的NOT NULL列,我有以下预部署脚本;
IF object_id('dbo.Supplier') IS NULL
BEGIN
CREATE TABLE [dbo].[Supplier]
(
[SupplierId] INT IDENTITY(1,1) NOT NULL,
[Name] varchar(50) NOT NULL,
CONSTRAINT [PK_Supplier] PRIMARY KEY CLUSTERED ([SupplierId])
);
SET IDENTITY_INSERT [dbo].[Supplier] ON;
INSERT INTO Supplier (SupplierId, Name) VALUES (1, 'Default Supplier')
SET IDENTITY_INSERT [dbo].[Supplier] OFF;
ALTER TABLE dbo.PickZone ADD SupplierId int NULL;
UPDATE PickZone SET SupplierId = 1
END
上面的脚本更新架构和数据,以确保发布发生时(我使用的是sqlpackage.exe(,当它将foriegn密钥约束应用于dbo.PickZone.SupplierId
:时不会失败
CREATE TABLE [dbo].[PickZone]
(
[PickZoneId] INT IDENTITY (1, 1) NOT NULL PRIMARY KEY,
[Name] VARCHAR(50) NOT NULL,
[SupplierId] INT NOT NULL,
CONSTRAINT [FK_PickZone_Supplier] FOREIGN KEY ([SupplierId]) REFERENCES [dbo].[Supplier] ([SupplierId])
)
问题是,vs2012Publish和sqlpackage.exe deploy似乎都准备了所需的架构更新,然后执行预部署脚本,然后执行架构更新-由于预部署脚本进行架构更改,这些更新现在不同步。
这会导致架构发布再次尝试添加表和列,并导致其失败。
很明显,我可以更改我的部署过程,在dacpac部署之外执行这些类型的准备脚本,但我有点希望dacpac负责所有架构更改。。。
有人知道如何发布dacpac来满足这种类型的更新吗?
我将描述我的解决方案。我们将使用部署dacpacs的sqlpackage工具。该工具支持许多参数。其中之一是GenerateSmartDefaults。请参见此处。
正如我所认为的,部署前脚本不必包含模式修改。只有SELECT、INSERT、UPDATE和DELETE语句。部署后脚本也是。
- 修改数据库架构(添加不带默认值的非null列或作为外键(
- 在部署后脚本中,为该列添加UPDATE语句并构建dacpac
- 使用SqlPackage工具部署此dacpac,如下所示:SqlPackage.exe/Action:Publish/SourceFile:your.dacpac/TargetServerName:serverName/TargetDatabaseName:databaseName/p:GenerateSmartDefaults=True
我认为您试图以错误的顺序执行此操作。默认情况下,SSDT在整个更新完成之前不会检查约束,即使在部署后脚本之后也是如此。这意味着您应该能够在项目中添加具有默认值和约束的表,在部署后脚本中发布数据插入/更新,并在完成所有操作后让SSDT启用FK约束。
如果你对此感到不舒服,你可以按照以下顺序进行:
- 创建表格
- 在部署后脚本中填充表
- 快照该架构
- -----------------
- 添加列(使用默认值(和FK约束
- 快照该架构
- -----------------
- 发布快照A
- 发布快照B
我认为,在大多数情况下,如果您的选项设置正确,以便在执行其他操作之后启用检查约束,则不必经历第二个过程。