4

我正在创建一个持续集成 MSBuild 脚本,它将本地 SQL Server 2012 中的数据库复制到 SQL Azure。

容易吧?

方法

经过大量研究,我遇到了以下方法:

  1. 使用 PowerShell 直接访问 DAC 库,然后使用MSBuild PowerShell 扩展来包装脚本。这将需要安装 PowerShell 3 并研究如何使 MSBuild PowerShell 扩展与它一起工作,因为显然 MS 将 DAC API 移动到了最新版本库中的不同命名空间。PowerShell 将提供对 API 的直接访问,但可能需要相当多的样板文件。

  2. 使用示例 DAC 框架客户端工具,这需要自己编译,因为 Codeplex 提供的下载仅包括托管版本。它还需要修复它们以使用 DAC 3.0 类,因为它们当前似乎使用的是早期版本的 DAC。<Exec Command="" />然后我可以从MSBuild 脚本中调用这些工具。更少的样板,如果我在路上遇到任何颠簸,我可以对源代码进行更改。

流程

无论使用哪种方法,该过程都可以是:

  1. 从本地 SQL Server 2012 导出到本地 BACPAC
  2. 将 BACPAC 上传到博客存储
  3. 通过托管 DAC将 BACPAC 导入 SQL Azure

或者:

  1. 从本地 SQL Server 2012 导出到本地 BACPAC
  2. 通过客户端 DAC将 BACPAC 导入 SQL Azure

问题

对于似乎是标准功能的东西,以上所有内容似乎都付出了很多努力......所以在我开始重新发明轮子并记录结果以供所有人查看之前,是否有一些非常明显的东西我错过了这里?是否有我尚未发现的 MS 已发布的预先编写的脚本?

SQL Server Management Studio 2012 的 GUI 中有一个命令完全符合我的要求(右键单击本地数据库,单击“任务”,单击“将数据库部署到 SQL Azure”)。当然,如果在 GUI 中单击几下,它一定是某处命令行上的单个命令?

4

1 回答 1

1

所以我决定使用 PowerShell(因为这实际上更像是一个脚本),只有客户端 DAC这里的例子非常有用。

使用MSBuild 扩展包允许创建任务来包装 PowerShell 样板。

请注意,您必须安装 PowerShell 3 才能访问 DAC 3.0,最好通过安装 Windows Management Framework 3.0 来实现。

TaskFactory 包装器如下:

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <!-- This custom task encapsulates DAC Framework 3.0 BACPAC import and export routines,
       which are compatible with SQL Server 2012 and later, and SQL Azure -->

  <!-- Required Import to use MSBuild Extension Pack -->
  <PropertyGroup>
    <AssemblyFile>$(MSBuildExtensionsPath)\ExtensionPack\4.0\MSBuild.ExtensionPack.TaskFactory.PowerShell.dll</AssemblyFile>
    <SqlServerDacDll>C:\Program Files (x86)\Microsoft SQL Server\110\DAC\bin\Microsoft.SqlServer.Dac.dll</SqlServerDacDll>
  </PropertyGroup>

  <UsingTask TaskFactory="PowershellTaskFactory" TaskName="ExportBacpac" AssemblyFile="$(AssemblyFile)">
    <ParameterGroup>
      <ConnectionString Required="true" ParameterType="System.String" />
      <BacpacFile Required="true" ParameterType="System.String" /> 
      <DatabaseName Required="true" ParameterType="System.String" />
    </ParameterGroup>
    <Task>
      <![CDATA[
            #write progress to activity log
            $log.LogMessage([Microsoft.Build.Framework.MessageImportance]"High","Starting export of database '$databasename' to '$bacpacfile' with connection string '$connectionstring' ")

            # load in DAC DLL (requires config file to support .NET 4.0)
            # change file location for a 32-bit OS
            add-type -path "$(SqlServerDacDll)"

            # make DacServices object, needs a connection string
            $d = new-object Microsoft.SqlServer.Dac.DacServices $connectionstring

            # register events, if you want 'em
            register-objectevent -in $d -eventname Message -source "msg" -action { $log.LogMessage([Microsoft.Build.Framework.MessageImportance]"High", $Event.SourceArgs[1].Message.Message) }

            # Export schema and data from database $databasename
            $d.exportbacpac($bacpacfile, $databasename)

            # clean up event
            unregister-event -source "msg"

            $log.LogMessage([Microsoft.Build.Framework.MessageImportance]"High","Completed export of database '$databasename' to '$bacpacfile'")
      ]]>
    </Task>
  </UsingTask>

  <UsingTask TaskFactory="PowershellTaskFactory" TaskName="ImportBacpac" AssemblyFile="$(AssemblyFile)">
    <ParameterGroup>
      <ConnectionString Required="true" ParameterType="System.String" />
      <BacpacFile Required="true" ParameterType="System.String" /> 
      <DatabaseName Required="true" ParameterType="System.String" /><!-- Not relevant for Azure import, which uses the Bacpac file name as the database name -->
    </ParameterGroup>
    <Task>
      <![CDATA[
            #write progress to activity log
            $log.LogMessage([Microsoft.Build.Framework.MessageImportance]"High","Starting import of database '$databasename' from '$bacpacfile' with connection string '$connectionstring' ")

            # load in DAC DLL (requires config file to support .NET 4.0)
            # change file location for a 32-bit OS
            add-type -path "$(SqlServerDacDll)"

            # make DacServices object, needs a connection string
            $d = new-object Microsoft.SqlServer.Dac.DacServices $connectionstring

            # register events, if you want 'em
            register-objectevent -in $d -eventname Message -source "msg" -action { $log.LogMessage([Microsoft.Build.Framework.MessageImportance]"High", $Event.SourceArgs[1].Message.Message) }

            # Load bacpac from file & import to database named $databasename
            $bp = [Microsoft.SqlServer.Dac.BacPackage]::Load($bacpacfile)
            $d.importbacpac($bp, $databasename)

            # clean up event
            unregister-event -source "msg"

            $log.LogMessage([Microsoft.Build.Framework.MessageImportance]"High","Completed import of database '$databasename' from '$bacpacfile'")
      ]]>
    </Task>
  </UsingTask>

</Project>

调用这些包装器的示例目标如下所示:

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="BacpacImportExport.xml"/>

  <PropertyGroup>
    <TempBacpacFile>$(ReleaseFolderPublish)\$(DestinationDBName).bacpac</TempBacpacFile>
  </PropertyGroup>

  <Target Name="CopyAndReplaceDatabaseViaBacpac">
    <Message Text="Clean bacpac directory"/>
    <Exec Command="mkdir $(ReleaseFolderPublish)\" IgnoreExitCode="true"></Exec>
    <Exec Command="del /Q $(ReleaseFolderPublish)\*.bacpac " IgnoreExitCode="true"></Exec>

    <MSBuild Projects="$(MSBuildProjectFile)" Targets="ReportBuildProgress" Properties="Message=Exporting database to BACPAC from source"/>
    <ExportBacpac
      ConnectionString="$(SourceConnectionString)"
      BacpacFile="$(TempBacpacFile)"
      DatabaseName="$(SourceDBName)"
    />


    <MSBuild Projects="$(MSBuildProjectFile)" Targets="ReportBuildProgress" Properties="Message=Dropping database from destination (does not fail on error)"/>
    <MSBuild.ExtensionPack.SqlServer.SqlCmd TaskAction="Execute" Server="$(DestinationDBServer)" Database="master"
                                            LogOn="$(DestinationDBUser)" Password="$(DestinationDBPass)"
                                            CommandLineQuery="DROP DATABASE [$(DestinationDBName)];"
                                            RedirectStandardError="true" SeverityLevel="1" />


    <MSBuild Projects="$(MSBuildProjectFile)" Targets="ReportBuildProgress" Properties="Message=Importing database from BACPAC to destination"/>
    <ImportBacpac
      ConnectionString="$(DestinationConnectionString)"
      BacpacFile="$(TempBacpacFile)"
      DatabaseName="$(DestinationDBName)"
    />

  </Target>
</Project>

这可以通过调用参考实现控制台应用程序来轻松修改为使用托管 DAC ,而不是调用.<Exec Command="" /><ImportBacpac />

如果您发现任何改进,请告诉我!

于 2012-09-25T21:08:31.413 回答