非web项目中使用XDT的实例

概述

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

 

posted @ 2016-08-03 17:30  cloversun  阅读(713)  评论(0编辑  收藏  举报