sql-serverdatabasedacpacdac

SQL Server DacFx to wrap 'DROP CONSTRAINT into 'IF EXISTS'


I use DacFx to deploy SQL Server DB Version1:

CREATE TABLE [dbo].[Person]
(
    [ID] [int] NOT NULL,
    [FirstName] [nvarchar](100) NOT NULL,
    [LastName] [nvarchar](100) NOT NULL

    CONSTRAINT [Clustered_Id] PRIMARY KEY CLUSTERED 
    (
        [ID]
    ) ON [PRIMARY],

    CONSTRAINT [UniqueName] UNIQUE NONCLUSTERED 
    (
        [LastName]
    ) ON [PRIMARY]
)

Later I need to deploy the Version2 with the only difference in the unique constraint - it must have an additional column in it - FirstName. When DacFx generates a script with the delta between the DB and the DacPac it deals with the index change by first removing the index and then creating it:

ALTER TABLE [dbo].[Person] DROP CONSTRAINT [UniqueName];
ALTER TABLE [dbo].[Person]
    ADD CONSTRAINT [UniqueName] UNIQUE NONCLUSTERED ([LastName] ASC, [FirstName] ASC) ON [PRIMARY];

Above I'm presenting just 1 table but in reality there are few more and when their indexes change the generated delta contains 2 blocks - one where the indexes are first dropped and 2nd where they indexes are created.

In normal scenarios the upgrade to the Version2 goes smoothly - but there could be a situation when an issue is encountered between the DROP CONSTRAINT and ADD CONSTRAINT and further script execution stops. This leads to issues when the upgrade script is re-run - this time the DROP CONSTRAINT fails because the index was already removed in the previous attempt.

How to deal with this situation without regenerating the delta script? Can I instruct the DacFx to wrap DROP CONSTRAINT into IF EXISTS ?


Solution

  • I've seen this question pop up a few times before on various forums. The standard response is..."that's not how SSDT works".

    It is state based deployment, so it is not intended to build re-runnable deployment scripts (though I admit it would be nice, even RedGate SQL Compare does, and it's great).

    Instead, if something broke, you would probably fix what needs to be fixed and re-run SqlPackage to re-generate a new script, or re-run the publish action in Visual Studio or using SqlPackage.

    One item you could consider, is using a DeploymentPlanModifier, which is a "Deployment Contributor" (Contributors are a feature of SSDT that allow you to add your own custom extensions to the build and deploy process), but this is where you start getting into the deep and murky waters of customizing SSDT deployments. I've tried going down this road...there aren't a lot of people doing this, so it will be hard finding answers to problems you run into.

    Here is an example from Microsoft themselves on how to build a "SqlRestartableScriptContributor", which sounds a lot like what you want to do and may be an option, but it requires having some skills building things in .NET.

    https://learn.microsoft.com/en-us/sql/ssdt/walkthrough-extend-database-project-deployment-to-modify-the-deployment-plan?view=sql-server-ver16