Using MSBuild to publish a VS 2012 SSDT .sqlproj database project
Post and code updated on March 21, 2013, and again on March 22, 2013.
We recently upgraded from VS (Visual Studio) 2010 to VS 2012, and with it had to upgrade our .dbproj database project to a .sqlproj. When making the switch I realized that .sqlproj database projects do not support specifying the database to deploy to as MSBuild command line arguments; instead you have to pass in the path to an xml file that has the necessary information.
So with the old .dbproj database project, you could deploy it to a database using:
MSBuild /t:Deploy /p:TargetDatabase="[DbName]";TargetConnectionString="Data Source=[Db.Server];Integrated Security=True;Pooling=False" /p:DeployToDatabase="True" "[PathToBranch]Database\Database.dbproj"
But with the new .sqlproj database project you have to do:
MSBuild /t:Publish /p:SqlPublishProfilePath="myPublishFile.publish.xml" "[PathToBranch]Database\Database.sqlproj"
Where “myPublishFile.publish.xml” contains the database server and name to publish to.
One other minor thing to note is that it is called “deploying” the database with .dbproj, and is called “publishing” the database with .sqlproj; so when I say Deploy or Publish, I mean the same thing.
We use TFS at my organization and while making new builds for our Test environment, we have the build process deploy the database solution to our various Test databases. This would mean that for us I would either need to:
1 – create a new [DbName].publish.xml file for each database, check it into source control, and update the build template to know about the new file, or
2 – update the file contents of our myPublishFile.publish.xml file dynamically during the build to replace the Database Name and Server in the file before publishing to the database (i.e. read in file contents, replace string, write file contents back to file, publish to DB, repeat).
Option 1 means more work every time I want to add a new Test database to publish to. Option 2 is better, but still means having to update my TF Build template and create a new activity to read/write the new contents to the file.
Instead, there is a 3rd option, which is to simply add the code below to the bottom of the .sqlproj file. This will add some new MSBuild targets to the .sqlproj that will allow us to specify the database name and connection string using similar MSBuild command line parameters that we used to deploy the .dbproj project.
The code presented here is based on this post, but the author has closed the comments section on that post and has not replied to my emails about the bugs in his code and example, so I thought I would share my modified and enhanced solution.
<!--
Custom targets and properties added so that we can specify the database to publish to using command line parameters with VS 2012 .sqlproj projects, like we did with VS 2010 .dbproj projects.
This allows us to specify the MSBuild command-line parameters TargetDatabaseName, and TargetConnectionString when Publishing, and PublishToDatabase when Building.
I also stumbled across the undocumented parameter, PublishScriptFileName, which can be used to specify the generated sql script file name, just like DeployScriptFileName used to in VS 2010 .dbproj projects.
Taken from: http://blog.danskingdom.com/using-msbuild-to-publish-a-vs-2012-ssdt-sqlproj-database-project-the-same-way-as-a-vs-2010-dbproj-database-project/
-->
<PropertyGroup Condition="'$(TargetDatabaseName)' != '' Or '$(TargetConnectionString)' != ''">
<PublishToDatabase Condition="'$(PublishToDatabase)' == ''">False</PublishToDatabase>
<TargetConnectionStringXml Condition="'$(TargetConnectionString)' != ''">
<TargetConnectionString xdt:Transform="Replace">$(TargetConnectionString)</TargetConnectionString>
</TargetConnectionStringXml>
<TargetDatabaseXml Condition="'$(TargetDatabaseName)' != ''">
<TargetDatabaseName xdt:Transform="Replace">$(TargetDatabaseName)</TargetDatabaseName>
</TargetDatabaseXml>
<TransformPublishXml>
<Project xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>$(TargetConnectionStringXml)$(TargetDatabaseXml)</PropertyGroup>
</Project>
</TransformPublishXml>
<SqlPublishProfilePath Condition="'$([System.IO.Path]::IsPathRooted($(SqlPublishProfilePath)))' == 'False'">$(MSBuildProjectDirectory)\$(SqlPublishProfilePath)</SqlPublishProfilePath>
<!-- In order to do a transform, we HAVE to change the SqlPublishProfilePath -->
<TransformOutputFile>$(MSBuildProjectDirectory)\Transformed_$(TargetDatabaseName).publish.xml</TransformOutputFile>
<TransformScope>$([System.IO.Path]::GetFullPath($(MSBuildProjectDirectory)))</TransformScope>
<TransformStackTraceEnabled Condition="'$(TransformStackTraceEnabled)'==''">False</TransformStackTraceEnabled>
</PropertyGroup>
<Target Name="AfterBuild" Condition="'$(PublishToDatabase)'=='True'">
<CallTarget Targets="Publish" />
</Target>
<UsingTask TaskName="ParameterizeTransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="BeforePublish" Condition="'$(TargetDatabaseName)' != '' Or '$(TargetConnectionString)' != ''">
<Message Text="TargetDatabaseName = '$(TargetDatabaseName)', TargetConnectionString = '$(TargetConnectionString)', PublishScriptFileName = '$(PublishScriptFileName)', Transformed Sql Publish Profile Path = '$(TransformOutputFile)'" Importance="high" />
<!-- If TargetDatabaseName or TargetConnectionString, is passed in then we use the tokenize transform to create a parameterized sql publish file -->
<Error Condition="!Exists($(SqlPublishProfilePath))" Text="The SqlPublishProfilePath '$(SqlPublishProfilePath)' does not exist, please specify a valid file using msbuild /p:SqlPublishProfilePath='Path'" />
<ParameterizeTransformXml Source="$(SqlPublishProfilePath)" IsSourceAFile="True" Transform="$(TransformPublishXml)" IsTransformAFile="False" Destination="$(TransformOutputFile)" IsDestinationAFile="True" Scope="$(TransformScope)" StackTrace="$(TransformStackTraceEnabled)" SourceRootPath="$(MSBuildProjectDirectory)" />
<PropertyGroup>
<SqlPublishProfilePath>$(TransformOutputFile)</SqlPublishProfilePath>
</PropertyGroup>
</Target>
So after adding this code at the bottom of the .sqlproj file (above the </Project> tag though), you can now build and publish the database solution from the MSBuild command line using:
MSBuild /t:Build /p:TargetDatabaseName="[DbName]";TargetConnectionString="Data Source=[Db.Server];Integrated Security=True;Pooling=False" /p:PublishToDatabase="True" /p:SqlPublishProfilePath="Template.publish.xml" "[PathToBranch]\Database\Database.sqlproj"
Here you can see the 3 new parameters that we’ve added being used: TargetDatabaseName, TargetConnectionString, and PublishToDatabase.
When the TargetDatabaseName or TargetConnectionString parameters are provided we generated a new transformed .publish.xml file, which is the same as the provided “Template.publish.xml” file, but with the database and connection string values replaced with the provided values.
The PublishToDatabase parameter allows us to publish to the database immediately after the project is built; without this you would have to first call MSBuild to Build the database project, and then call MSBuild again to Publish it (or perhaps using “/t:Build;Publish” would work, but I didn’t test that).
If you want to simply publish the database project without building first (generally not recommended), you can do:
MSBuild /t:Publish /p:TargetDatabaseName="[DbName]";TargetConnectionString="Data Source=[Db.Server];Integrated Security=True;Pooling=False" /p:SqlPublishProfilePath="Template.publish.xml" "[PathToBranch]\Database\Database.sqlproj"
Be careful though, since if you don’t do a Build first, any changes that have been made since the last time the .sqlproj file was built on your machine won’t be published to the database.
Notice that I still have to provide a path to the template publish.xml file to transform, and that the path to this file is relative to the .sqlproj file (in this example the Template.publish.xml and .sqlproj files are in the same directory). You can simply use one of the publish.xml files generated by Visual Studio, and then the TargetDatabaseName and TargetConnectionString xml element values will be replaced with those given in the command line parameters. This allows you to still define any other publish settings as usual in the xml file.
Also notice that the PublishToDatabase parameter is only used when doing a Build, not a Publish; providing it when doing a Publish will not hurt anything though.
While creating my solution, I also accidentally stumbled upon what seems to be an undocumented SSDT parameter, PublishScriptFileName. While the DeployScriptFileName parameter could be used in VS 2010 .dbproj projects to change the name of the generated .sql file, I noticed that changing its value in the .publish.xml file didn’t seem to have any affect at all (so I’m not really sure why Visual Studio puts it in there). I randomly decided to try passing in PublishScriptFileName from the command line, and blamo, it worked! I tried changing the <DeployScriptFileName> element in the .publish.xml file to <PublishScriptFileName>, but it still didn’t seem to have any effect.
So now if I wanted to deploy my database project to 3 separate databases, I could do so with the following code to first Build the project, and the Publish it to the 3 databases:
MSBuild /t:Build "[PathToBranch]\Database\Database.sqlproj"
MSBuild /t:Publish /p:TargetDatabaseName="[DbName1]";TargetConnectionString="Data Source=[Db.Server];Integrated Security=True;Pooling=False" /p:PublishScriptFileName="[DbName1].sql" /p:SqlPublishProfilePath="Template.publish.xml" "[PathToBranch]\Database\Database.sqlproj"
MSBuild /t:Publish /p:TargetDatabaseName="[DbName2]";TargetConnectionString="Data Source=[Db.Server];Integrated Security=True;Pooling=False" /p:PublishScriptFileName="[DbName2].sql" /p:SqlPublishProfilePath="Template.publish.xml" "[PathToBranch]\Database\Database.sqlproj"
MSBuild /t:Publish /p:TargetDatabaseName="[DbName3]";TargetConnectionString="Data Source=[Db.Server];Integrated Security=True;Pooling=False" /p:PublishScriptFileName="[DbName3].sql" /p:SqlPublishProfilePath="Template.publish.xml" "[PathToBranch]\Database\Database.sqlproj"
You could also instead just call MSBuild using the Build target with the PublishToDatabase parameter (which might actually be the safer bet); whatever you prefer. I have found that once the database project is built once, as long as no changes are made to it then subsequent “builds” of the project only take a second or two since it detects that no changes have been made and skips doing the build.
If you have any questions or feedback, let me know.
Happy coding!
Using MSBuild to publish a VS 2012 SSDT .sqlproj database project的更多相关文章
- Headless MSBuild Support for SSDT (*.sqlproj) Projects
http://sqlproj.com/index.php/2012/03/headless-msbuild-support-for-ssdt-sqlproj-projects/ Update: bre ...
- Headless MSBuild Support for SSDT (*.sqlproj) Projects [利用msbuild自动化部署 .sqlproj]- 摘自网络
Update: breaking change: http://sqlproj.com/index.php/2012/10/dacfx-sept-2012-updates-break-headless ...
- use MSBuild cmd to build and deploy the database project.
You can use the below command to implement it. SET TargetDBName="Database name" SET DataBa ...
- Visual Studio 2012创建SQL Server Database Project提示失败解决方法
新建一个SQL Server Database Project,提示: Unable to open Database project This version of SQL Server Data ...
- vs 2012 InstallShield Limited Edition Project 打包windows服务解析
最近项目从vs2005 升级到vs2010后,发现新的vs2010 不再带有原来的安装工程项目,导致以前的安装包不可以使用,查找资料后发现微软从vs2010 版本后不再提供自带的安装工程,尝试着利用 ...
- 如何使用SSDT进行SQL数据库的自动化部署到生产环境和版本控制
简介 在开发过程,我们常常会遇到数据库环境部署的问题,当部署正式环境中,数据库产生的多文件脚本在管理上就容易出现混乱,特别是你还没有权限访问正式环境的情况,就更为困难.SSDT为我们提供了很好的解决方 ...
- [独孤九剑]持续集成实践(二)– MSBuild语法入门
本系列文章包含: [独孤九剑]持续集成实践(一)- 引子 [独孤九剑]持续集成实践(二)– MSBuild语法入门 [独孤九剑]持续集成实践(三)- Jenkins安装与配置(Jenkins+MSBu ...
- 在.NET 环境中实现每日构建(Daily Build)--ccnet,MSBuild篇(转载)
每日构建,对我们团队来说一个全新的概念.随着项目开发的进展,在开发过 程需要及时反馈一些BUG和功能要求的处理情况.而在这种情况下每天或隔一段时间Build一个版本,工作量还是比较大的,所以就特别有必 ...
- 用MSBuild和Jenkins搭建持续集成环境(1)
http://www.infoq.com/cn/articles/MSBuild-1 你或其他人刚刚写完了一段代码,提交到项目的版本仓库里面.但等一下,如果新提交的代码把构建搞坏了怎么办?万一出现编 ...
随机推荐
- [LintCode] Maximum Gap 求最大间距
Given an unsorted array, find the maximum difference between the successive elements in its sorted f ...
- [CareerCup] 16.3 Dining Philosophers 哲学家聚餐问题
16.3 In the famous dining philosophers problem, a bunch of philosophers are sitting around a circula ...
- strcat、strcpy、memcpy 的使用注意
char *p = "hello";//5 个长度 ; //char *str = (char *)malloc(sizeof(char)*len); ] = "niha ...
- Xstream(对象和xml转换)
package com.vcredit.framework.utils; import java.io.Writer; import org.apache.commons.lang3.StringUt ...
- 浅谈java性能分析
浅谈java性能分析,效能分析 在老师强烈的要求下做了效能分析,对上次写过的词频统计的程序进行分析以及改进. 对于效能分析:我个人很浅显的认为就是程序的运行效率,代码的执行效率等等. java做性能测 ...
- Color Space: HSV
HSV(hue,saturation,value)颜色空间的模型对应于圆柱坐标系中的一个圆锥形子集,圆锥的顶面对应于V=1. 它包含RGB模型中的R=1,G=1,B=1 三个面,所代表的颜色较亮.色彩 ...
- Graph | Eulerian path
In graph theory, a Eulerian trail (or Eulerian path) is a trail in a graph which visits every edge e ...
- Final-阶段站立会议3
组名:天天向上 组长:王森 组员:张政.张金生.林莉.胡丽娜 代码地址:HTTPS:https://git.coding.net/jx8zjs/llk.git SSH:git@git.coding.n ...
- DataGridView key points
Simple Examples => http://csharp.net-informations.com/datagridview/csharp-datagridview-readonly-c ...
- win7下用mklink命令解决delphiXE系列占用C盘空间的问题
DelphiXE从2010以后, 安装程序安装完成后都会在ProgramData目录里复制一份安装程序的备份, 随着版本升级安装包越来越大, 占用C盘的空间也就越来越大 虽然可以通过删除的方式删掉, ...