Skip to content
kendaleiv.com
Go back

Previewing DACPAC Deployments By Generating Deployment Reports And Scripts

A DACPAC is a method for deploying a database via code. You specify the desired state of the database and the tooling determines how to modify the target database to match the desired state. (see https://learn.microsoft.com/en-us/sql/relational-databases/data-tier-applications/data-tier-applications for more details)

You can preview what would happen if a specific DACPAC would be deployed to a database.

If you’re using SqlPackage you can specify SqlPackage /Action:DeployReport for an XML report or SqlPackage /Action:Script for T-SQL script (see https://learn.microsoft.com/en-us/sql/tools/sqlpackage/sqlpackage-deploy-drift-report / https://learn.microsoft.com/en-us/sql/tools/sqlpackage/sqlpackage-script).

Tests

using Microsoft.SqlServer.Dac;
using ThrowawayDb;
using Xunit;

namespace TestDatabase.Tests
{
    public class PreviewTests
    {
        [Fact]
        public void DacServicesGenerateDeployReport()
        {
            // Arrange
            using var database = ThrowawayDatabase.FromLocalInstance(@"(LocalDB)\MSSQLLocalDB");

            var dacPackage = DacPackage.Load("../../../../../src/TestDatabase/bin/Debug/TestDatabase.dacpac");
            var dacServices = new DacServices(database.ConnectionString);

            // Act
            var deployReport = dacServices.GenerateDeployReport(dacPackage, database.Name);

            // Assert
            Assert.Equal(
                @"<?xml version=""1.0"" encoding=""utf-8""?><DeploymentReport xmlns=""http://schemas.microsoft.com/sqlserver/dac/DeployReport/2012/02""><Alerts /><Operations><Operation Name=""Create""><Item Value=""[dbo].[TestTable]"" Type=""SqlTable"" /></Operation></Operations></DeploymentReport>",
                deployReport);
        }

        [Fact]
        public void DacServicesGenerateDeployScript()
        {
            // Arrange
            using var database = ThrowawayDatabase.FromLocalInstance(@"(LocalDB)\MSSQLLocalDB");

            var dacPackage = DacPackage.Load("../../../../../src/TestDatabase/bin/Debug/TestDatabase.dacpac");
            var dacServices = new DacServices(database.ConnectionString);

            // Act
            var deployScript = dacServices.GenerateDeployScript(dacPackage, database.Name);

            // Assert
            var user = "USER_HERE";
            Assert.Equal(
$@"/*
Deployment script for {database.Name}

This code was generated by a tool.
Changes to this file may cause incorrect behavior and will be lost if
the code is regenerated.
*/

GO
SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON;

SET NUMERIC_ROUNDABORT OFF;


GO
:setvar DatabaseName ""{database.Name}""
:setvar DefaultFilePrefix ""{database.Name}""
:setvar DefaultDataPath ""C:\Users\{user}\AppData\Local\Microsoft\Microsoft SQL Server Local DB\Instances\MSSQLLocalDB\""
:setvar DefaultLogPath ""C:\Users\{user}\AppData\Local\Microsoft\Microsoft SQL Server Local DB\Instances\MSSQLLocalDB\""

GO
:on error exit
GO
/*
Detect SQLCMD mode and disable script execution if SQLCMD mode is not supported.
To re-enable the script after enabling SQLCMD mode, execute the following:
SET NOEXEC OFF; 
*/
:setvar __IsSqlCmdEnabled ""True""
GO
IF N'$(__IsSqlCmdEnabled)' NOT LIKE N'True'
    BEGIN
        PRINT N'SQLCMD mode must be enabled to successfully execute this script.';
        SET NOEXEC ON;
    END


GO
IF EXISTS (SELECT 1
           FROM   [master].[dbo].[sysdatabases]
           WHERE  [name] = N'$(DatabaseName)')
    BEGIN
        ALTER DATABASE [$(DatabaseName)]
            SET AUTO_CLOSE OFF 
            WITH ROLLBACK IMMEDIATE;
    END


GO
USE [$(DatabaseName)];


GO
IF EXISTS (SELECT 1
           FROM   [master].[dbo].[sysdatabases]
           WHERE  [name] = N'$(DatabaseName)')
    BEGIN
        ALTER DATABASE [$(DatabaseName)]
            SET ANSI_NULLS ON,
                ANSI_PADDING ON,
                ANSI_WARNINGS ON,
                ARITHABORT ON,
                CONCAT_NULL_YIELDS_NULL ON,
                QUOTED_IDENTIFIER ON,
                ANSI_NULL_DEFAULT ON,
                CURSOR_DEFAULT LOCAL,
                RECOVERY FULL 
            WITH ROLLBACK IMMEDIATE;
    END


GO
IF EXISTS (SELECT 1
           FROM   [master].[dbo].[sysdatabases]
           WHERE  [name] = N'$(DatabaseName)')
    BEGIN
        ALTER DATABASE [$(DatabaseName)]
            SET PAGE_VERIFY NONE,
                DISABLE_BROKER 
            WITH ROLLBACK IMMEDIATE;
    END


GO
ALTER DATABASE [$(DatabaseName)]
    SET TARGET_RECOVERY_TIME = 0 SECONDS 
    WITH ROLLBACK IMMEDIATE;


GO
IF EXISTS (SELECT 1
           FROM   [master].[dbo].[sysdatabases]
           WHERE  [name] = N'$(DatabaseName)')
    BEGIN
        ALTER DATABASE [$(DatabaseName)]
            SET QUERY_STORE (CLEANUP_POLICY = (STALE_QUERY_THRESHOLD_DAYS = 367)) 
            WITH ROLLBACK IMMEDIATE;
    END


GO
PRINT N'Creating SqlTable [dbo].[TestTable]...';


GO
CREATE TABLE [dbo].[TestTable] (
    [Id] INT NOT NULL,
    PRIMARY KEY CLUSTERED ([Id] ASC)
);


GO
PRINT N'Update complete.';


GO
",
deployScript);
        }
    }
}

Demo

These tests are available at https://github.com/kendaleiv/dacpac-playground/blob/main/tests/TestDatabase.Tests/PreviewTests.cs.


Share this post:

Previous Post
Operating A Wi-Fi Enabled Robot Vacuum / Mop During A Home Internet Outage Using A Smartphone Hotspot
Next Post
DACPAC Deployments Can Delete/Drop Indexes Not In DACPAC By Default