DACPAC模式比较在发布期间的预部署脚本之前运行

时间:2015-09-01 11:41:38

标签: sql-server visual-studio-2013 sql-server-2014 msdeploy sql-server-data-tools

使用sqlpackage.exe发布dacpac时,它首先运行Schema Compare,然后运行预部署脚本。例如,当您需要删除表或重命名列时,这会导致问题。架构比较是在修改对象和部署失败之前完成的。必须重复发布才能考虑新模式。

任何人都有解决方法,不涉及两次发布?

3 个答案:

答案 0 :(得分:8)

Gert Drapers将其称为部署前脚本here

实际上这是一个挑战。如果需要将非可空和外键列添加到充满数据的表中 - 您只能使用单独的脚本。

如果您是唯一的开发人员 - 这不是问题,但是当您拥有一个大型团队时,必须在每次数据库发布之前以某种方式执行“单独脚本”。

我们使用的解决方法:

  • 创建单独的SQL“Before-publish”脚本(在DB项目中),该脚本具有[Build action = None]
  • 属性
  • 创建custom MSBuild Task在哪里调用SQLCMD.EXE实用程序,将“Before-publish”脚本作为参数传递,然后调用SQLPACKAGE.EXE实用程序传递DB.dacpac
  • 将自定义MSBuild任务调用添加到db.sqlproj文件。例如:
<UsingTask 
        TaskName="MSBuild.MsSql.DeployTask" 
        AssemblyFile="$(MSBuildProjectDirectory)\Deploy\MsBuild.MsSql.DeployTask.dll" />

<Target Name="AfterBuild">
    <DeployTask 
        Configuration="$(Configuration)" 
        DeployConfigPath="$(MSBuildProjectDirectory)\Deploy\Deploy.config" 
        ProjectDirectory="$(MSBuildProjectDirectory)" 
        OutputDirectory="$(OutputPath)" 
        DacVersion="$(DacVersion)">
    </DeployTask>
</Target>
上面的

MsBuild.MsSql.DeployTask.dll 是自定义MSBuild任务。

因此可以从Visual Studio调用“Before-publish”脚本。

对于CI,我们使用批处理文件(* .bat),其中调用了相同的两个实用程序(SQLCMD.EXE&amp; SQLPACKAGE.EXE)。

我们得到的最后一个过程有点复杂,应该在另一篇文章中描述 - 这里我只提到了一个方向:)

答案 1 :(得分:1)

从使用visual studio转移到使用驱动sqlpackage.exe的脚本,您可以在比较之前灵活地运行脚本:

https://the.agilesql.club/Blog/Ed-Elliott/Pre-Deploy-Scripts-In-SSDT-When-Are-They-Run

答案 2 :(得分:1)

我们遇到了在部署数据库项目期间需要将数据从一个表转换为另一个表的情况。当然,使用数据库项目是一个问题,因为在预部署中目标表(列)仍然不存在,但在部署后脚本中,源表(列)已经不存在。

要将数据从TableA转换为TableB,我们使用了以下想法(此方法可用于任何数据修改):

  1. 开发人员将目标表(dbo.TableB)添加到数据库项目中并将其部署到本地数据库(无需提交到SVN)
  2. 他或她创建预部署转换脚本。诀窍是脚本将结果数据放入临时表:#TableB
  3. 开发人员删除数据库项目中的dbo.TableA。假设在执行主生成脚本期间将删除该表。
  4. 开发人员编写一个部署后脚本,将#TableB的数据复制到刚刚由主脚本创建的dbo.TableB。
  5. 所有更改都已提交到SVN。
  6. 由于我们将中间数据存储在临时表中,因此我们不需要预部署前脚本。

    我想说使用预部署前脚本的方法具有相同的中间(临时)数据,但它不存储在临时表中,而是存储在实际表中。它发生在预部署之前和预部署之间。执行预部署脚本后,此中间数据​​将消失。

    更重要的是,使用临时表的方法使我们能够面对以下复杂但真实的情况:想象一下,我们的数据库项目中有两个转换:

    1. 表A - &gt;表B
    2. 表B - &gt;表C
    3. 除此之外,我们有两个数据库:

      1. 具有TableA
      2. 的DatabaeA
      3. DatabaeB,其中TableA已经转换为TableB。 TableA在DatabaseB中不存在。
      4. 尽管如此,我们可以处理这种情况。我们在预部署中只需要一个新操作。在转换之前,我们尝试将数据从dbo.TableA复制到#TableA。转换脚本仅适用于临时表。

        让我向您展示这个想法在DatabaseA和DatabaseB中是如何工作的。 假设DB项目具有两对部署前和后部脚本:&#34; TableA - &gt;表B&#34;和&#34;表B - &gt;表C&#34 ;.

        以下是&#34; TableB - &gt;的脚本示例。表C&#34;转化

        预部署脚本

        ----[The data preparation block]---
        --We must prepare to possible transformation
        --The condition should verufy the existance of necessary columns
        IF  OBJECT_ID('dbo.TableB') IS NOT NULL AND
            OBJECT_ID('tempdb..#TableB') IS NULL
        BEGIN
            CREATE TABLE #TableB
            (
                [Id] INT NOT NULL PRIMARY KEY, 
                [Value1] VARCHAR(50) NULL, 
                [Value2] VARCHAR(50) NULL
            )
        
            INSERT INTO [#TableB]
            SELECT [Id], [Value1], [Value2]
            FROM dbo.TableB
        END
        
        ----[The data transformation block]---
        --The condition of the transformation start
        --It is very important. It must be as strict as posible to ward off wrong executions.
        --The condition should verufy the existance of necessary columns
        --Note that the condition and the transformation must use the #TableA instead of dbo.TableA
        IF  OBJECT_ID('tempdb..#TableB') IS NOT NULL 
        BEGIN
        
            CREATE TABLE [#TableC]
            (
                [Id] INT NOT NULL PRIMARY KEY, 
                [Value] VARCHAR(50) NULL
            )
        
            --Data transformation. The source and destimation tables must be temporary tables.
            INSERT INTO [#TableC]
            SELECT [Id], Value1 + ' '+ Value2 as Value
            FROM [#TableB]
        
        END
        

        部署后脚本

        --Here must be a strict condition to ward of a failure
        --Checking of the existance of fields is a good idea
        IF  OBJECT_ID('dbo.TableC') IS NOT NULL AND
            OBJECT_ID('tempdb..#TableC') IS NOT NULL
        BEGIN
        
            INSERT INTO [TableC]
            SELECT [Id], [Value]
            FROM [#TableC]
        
        END
        

        在DatabaseA中,预部署脚本已经创建了#TableA。因此,由于数据库中没有dbo.TableB,数据准备块不会被执行。 但是,将执行数据转换,因为数据库中的#TableA是由&#34; TableA - &gt;的转换块创建的。表B&#34;

        在DatabaseB中,&#34; TableA - &gt;的数据准备和转换块。表B&#34;脚本不会被执行。但是我们已经在dbo.TableB中获得了转换后的数据。因此,&#34; TableB - &gt;的数据准备和转换块。表C&#34;将毫无问题地执行。