概述
XDT是Asp.net 4.0中的一个特性,可以让使用者在Web项目中在不同的生成类型下,快速切换配置文件(如在debug场景下使用测试配置数据库,在Release场景下使用正式配置数据库)。 但在非web项目中,VS并未提供如此方便的功能。这时如果我们同样想使用xdt transforms的功能,就需要自己配置MSbuild文件。
在本例中我们是通过修改MSBuild配置文件,来使非web项目同样可以使用XDT功能。
MSBuild全称(Microsoft Build Engine),是用于构建应用程序的平台。可以把他简单的理解成Vs生成时的项目用配置,可以利用其中的配置信息对项目文件实施特定顺序的操作。
项目准备
新建一个控制台项目,并在项目中创建如下的文件:
其中app.config的内容
<connectionStrings> <add name="DefaultConnection" connectionString="Data Source=TestSQLServer;Initial Catalog=MyTestDB;Integrated Security=True" providerName="System.Data.SqlClient"/> </connectionStrings>
app.debug.config的内容
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<connectionStrings> <add name="DefaultConnection" connectionString="Data Source=Debug;Initial Catalog=MydebugDB;Integrated Security=True" providerName="System.Data.debug" xdt:Transform="Replace" xdt:Locator="Match(name)"/> </connectionStrings>
app.release.config的内容
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<connectionStrings> <add name="DefaultConnection" connectionString="Data Source=Debug;Initial Catalog=MydebugDB;Integrated Security=True" providerName="System.Data.debug" xdt:Transform="Replace" xdt:Locator="Match(name)"/> </connectionStrings>
在控制台程序中输出你想看到的变化的配置文件
private static readonly string DifferentConfigString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString; static void Main(string[] args) { Console.Write(DifferentConfigString); Console.Read(); }
整个的项目结构最后的呈现如下的样子(config会在具体配置后才变成如下的样子):
具体实现:
1. 项目中引用Msbuild和Microsoft.dotnet.xdt.tools
2. 在系统创建%ProgramFiles (x86)%\MSBuild\Custom文件夹,把本文末尾的TransformFiles.targets文件拷贝到文件夹中,注意要修改文件中的版本号跟你安装的Msbuild版本号一致。(本文中为简化例子,只介绍这一种配置方式,团队协作的配置或者单独项目的配置会在后续的文章中说明)
3. 右键点击项目->卸载项目->右键点击项目->编辑.csproj 项目文件
4. 在项目文件结尾的</Project> tag上,插入如下代码
<Import Project="$(MSBuildExtensionsPath)\Custom\TransformFiles.targets" />
5. 在项目文件中,给那些你想转换的文件一个metadata,把TransformOnBuild设置成true.
在本例中,修改项目文件中的<None Include="App.config" />为
<None Include="App.config"> <TransformOnBuild>true</TransformOnBuild> </None>
修改<None Include="App.Debug.config" /><None Include="App.Release.config" />为
<None Include="App.Debug.config"> <DependentUpon>app.config</DependentUpon> </None> <None Include="App.Release.config"> <DependentUpon>app.config</DependentUpon> </None>
6. 重新加载项目
7. 切换解决方案配置,运行程序发现配置文件已经会自动使用不同的app.config中的内容
示例效果
在debug环境下自动使用debug参数
在release版本里自动使用release参数
实现原理
TransformFile.targets 含有两个目标(target):DiscoverFilesToTransform,TransformAllFiles。
其中。DiscoverFilesToTransform会浏览所有的项 (None, Content, and Resource)。 在DiscoverFilesToTransform我查找含有%(TransformOnBuild)==true的值。当收集到所有的值以后,识别出是否有一个“app.config”会被转换,如果是,把它放在一个特殊的项列表中,并且把其他的放在另一个项列表中。
在TransformAllFiles 中,TransformXml 任务被用来转换所有的文件。使用属性AfterTargets="Build;_CopyAppConfigFile",目标得以把自己注入到生成进程中。 每当生成或者_CopyAppConfigFile目标被调用,TransformAllFiles目标会被执行
以下是TransformFiles.targets文件的所有代码
<?xml version="1.0" encoding="utf-8"?> <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.Publishing.Tasks.dll"/> <ItemDefinitionGroup> <!-- Set the default value to false here --> <None> <TransformOnBuild>false</TransformOnBuild> </None> <Content> <TransformOnBuild>false</TransformOnBuild> </Content> <Resource> <TransformOnBuild>false</TransformOnBuild> </Resource> <EmbeddedResource> <TransformOnBuild>false</TransformOnBuild> </EmbeddedResource> <_FilesToTransform> <IsAppConfig>false</IsAppConfig> </_FilesToTransform> </ItemDefinitionGroup> <PropertyGroup> <TransformAllFilesDependsOn> DiscoverFilesToTransform; </TransformAllFilesDependsOn> </PropertyGroup> <Target Name="TransformAllFiles" DependsOnTargets="$(TransformAllFilesDependsOn)" AfterTargets="Build;_CopyAppConfigFile"> <!-- Now we have the item list _FilesToTransformNotAppConfig and _AppConfigToTransform item lists --> <!-- Transform the app.config file --> <ItemGroup> <_AppConfigTarget Include="@(AppConfigWithTargetPath->'$(OutDir)%(TargetPath)')" /> </ItemGroup> <PropertyGroup> <_AppConfigDest>@(_AppConfigTarget->'%(FullPath)')</_AppConfigDest> </PropertyGroup> <MakeDir Directories="@(_FilesToTransformNotAppConfig->'$(OutDir)%(RelativeDir)')" Condition="Exists('%(RelativeDir)%(Filename).$(Configuration)%(Extension)')"/> <TransformXml Source="@(_AppConfigToTransform->'%(FullPath)')" Transform="%(RelativeDir)%(Filename).$(Configuration)%(Extension)" Destination="$(_AppConfigDest)" Condition=" Exists('%(RelativeDir)%(Filename).$(Configuration)%(Extension)') " /> <TransformXml Source="@(_FilesToTransformNotAppConfig->'%(FullPath)')" Transform="%(RelativeDir)%(Filename).$(Configuration)%(Extension)" Destination="@(_FilesToTransformNotAppConfig->'$(OutDir)%(RelativeDir)%(Filename)%(Extension)')" Condition=" Exists('%(RelativeDir)%(Filename).$(Configuration)%(Extension)') " /> </Target> <Target Name="DiscoverFilesToTransform"> <!-- This will look through items list: None & Content for those with Metadata <TransformOnBuild>True</TransformOnBuild> --> <ItemGroup> <_FilesToTransform Include="@(None);@(Content);@(Resource);@(EmbeddedResource)" Condition=" '%(TransformOnBuild)' == 'true' "/> </ItemGroup> <PropertyGroup> <_AppConfigFullPath>@(AppConfigWithTargetPath->'%(RootDir)%(Directory)%(Filename)%(Extension)')</_AppConfigFullPath> </PropertyGroup> <!-- Now look to see if any of these are the app.config file --> <ItemGroup> <_FilesToTransform Condition=" '%(FullPath)'=='$(_AppConfigFullPath)' "> <IsAppConfig>true</IsAppConfig> </_FilesToTransform> </ItemGroup> <ItemGroup> <_FilesToTransformNotAppConfig Include="@(_FilesToTransform)" Condition=" '%(IsAppConfig)'!='true'"/> <_AppConfigToTransform Include="@(_FilesToTransform)" Condition=" '%(IsAppConfig)'=='true'"/> </ItemGroup> </Target> </Project>
也可以在这里下载
其他
了解XDT:http://www.cnblogs.com/JustRun1983/p/3418844.html
了解MSbuild:http://www.cnblogs.com/l_nh/archive/2012/08/30/2662648.html
本文来源于:http://sedodream.com/2010/11/18/XDTWebconfigTransformsInNonwebProjects.aspx