Donnerstag, 28. April 2016

web.config-transform - "my way"

Ich mag die Web.Config-Transforms. Sehr sogar.
Was mich daran stört ist, dass ich eine Build-Konfiguration für jede Stage/jeden Server benötige. Um das zu umgehen (und ggf. vielleicht auch einmal andere xml-Dateien zu transformieren) kann die Transformation auch manuell über einen MSBuild-Task starten.
Im Regelfall habe ich meine Transformationen im "Configs\"-Verzeichnis mit der Benennung web.[stage].conf - die lasse ich dann automatisiert beim build & publish erstellen. Dafür habe ich das folgende targets-file, das ich einfach im csproj einbinde:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <SourceWebConfig>$(ProjectDir)\Web.config</SourceWebConfig>
        <OutputPath Condition="'$(OutputPath)' == ''">$(ProjectDir)\bin</OutputPath>
        <TempWebConfig>$(OutputPath)\Web.Temp.config</TempWebConfig>
        <TransformationsBaseDir>$(ProjectDir)\Configs</TransformationsBaseDir>
        <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">11.0</VisualStudioVersion>
        <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
    </PropertyGroup>
    <Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets"/>

    <ItemGroup>
        <WebConfigTransforms Include="$(TransformationsBaseDir)/*.config">
            <TransformDest>$(OutputPath)\%(WebConfigTransforms.Filename).config</TransformDest>
        </WebConfigTransforms>
    </ItemGroup>

    <Target Name="TransformAllWebConfigs">
        <!-- Does the real transformation of all web-config transforms... -->
        <Copy SourceFiles="$(SourceWebConfig)"
              DestinationFiles="$(TempWebConfig)" />
        <Message Text="transforming %(WebConfigTransforms.Identity) to %(WebConfigTransforms.TransformDest)"
                 Importance="high"/>
        <TransformXml Source="$(TempWebConfig)"
                      Transform="%(WebConfigTransforms.Identity)"
                      Destination="%(WebConfigTransforms.TransformDest)"
                      StackTrace="true" />
        <Delete Files="$(TempWebConfig)" />
    </Target>

    <Target Name="TransformAfterBuild"
            AfterTargets="Build">
        <!-- after build, build all web.configs, too -->
        <CallTarget Targets="TransformAllWebConfigs" />
        <Message Text="TransformAllWebConfigs has run after build!"
                 Importance="high"/>
    </Target>
    <Target Name="MimicVsTransforms"
            AfterTargets="PreTransformWebConfig">
        <!-- this should mimic the original VS-behaviour -->
        <CallTarget Targets="TransformAllWebConfigs" />
    </Target>
    <Target Name="CopyTransformedForPublish"
            AfterTargets="CopyAllFilesToSingleFolderForPackage">
        <!-- "GatherAllFilesToPublish" as AfterTarges seems to work, too. But only when Publish is called from within VS. -->
        <Copy SourceFiles="@(WebConfigTransforms->'%(TransformDest)')"
              DestinationFolder="$(WPPAllFilesInSingleFolder)" />
    </Target>
</Project>

Die Einbindung im projekt-file erfolgt ganz "normal".
    <Import Project="$(ProjectDir)\..\TransformWebConfig.targets" Condition="'$(TransformationsBaseDir)' == ''" />

In einem aktuellen Projekt bestand die Anforderung die Transformationen in Unterverzeichnissen abzulegen (da es noch mehr spezifische Dateien pro Stage gab...). Dafür habe einfach die ItemGruop wie folgt angepasst:
<ItemGroup>
 <WebConfigTransforms Include="$(TransformationsBaseDir)\**\web.config">
  <StageName>$([System.String]::new('%(RecursiveDir)').TrimEnd('\\'))</StageName>
  <TransformDest>$(OutputPath)\web.%(StageName).config</TransformDest>
 </WebConfigTransforms>
</ItemGroup>

Keine Kommentare:

Kommentar veröffentlichen