How to transform log4net config like web.config?

O.O picture O.O · Apr 5, 2011 · Viewed 11k times · Source

From my .csproj file:

<Content Include="log4net.config">
  <SubType>Designer</SubType>
</Content>
<Content Include="log4net.Release.config">
  <DependentUpon>log4net.config</DependentUpon>
</Content>
<Content Include="log4net.Debug.config">
  <DependentUpon>log4net.config</DependentUpon>
</Content>
<Content Include="log4net.Live.config">
  <DependentUpon>log4net.config</DependentUpon>   
</Content>
<Content Include="log4net.Demo.config">
  <DependentUpon>log4net.config</DependentUpon>   
</Content>  

At the bottom of my .csproj file:

  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
    <Target Name="AfterCompile" Condition="exists('log4net.$(Configuration).config')">
      <TransformXml Source="log4net.config"
        Destination="$(IntermediateOutputPath)$(TargetFileName).config"
        Transform="log4net.$(Configuration).config" />
      <ItemGroup>
        <AppConfigWithTargetPath Remove="log4net.config"/>
        <AppConfigWithTargetPath Include="$(IntermediateOutputPath)$(TargetFileName).config">
          <TargetPath>$(TargetFileName).config</TargetPath>
        </AppConfigWithTargetPath>
      </ItemGroup>
    </Target>

From log4net.config

<connectionString name="ConnName" 
value="Data Source=localhost\sqlexpress;Initial Catalog=localdb;Persist Security Info=True;Integrated Security=SSPI;" />

From log4net.Live.config (removed sensitive data)

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
    <connectionString name="ConnName" value="Data Source=127.0.0.1;Initial Catalog=DBName;Persist Security Info=True;User ID=userid;Password=pword"
        providerName="System.Data.SqlClient" xdt:Transform="Replace" xdt:Locator="Match(name)" />
</configuration>

I checked msbuild output and I see that it transformed my web.config correctly, but I see no output for it transforming log4net. Also, when I check the log4net.config file after publish, it has the original connection string.

What am I doing wrong :)?

Thanks!

Update

I had some errors in the code that msbuild were outputting as warnings that I didn't see. I fixed those and now I get some output from MSBuild:

AfterCompile: Transforming Source File: log4net.config Applying Transform File: log4net.Live.config Output File: obj\Live\Common.UI.Web.dll.config
Transformation succeeded

This is still a problem, because the file should be named log4net.config, not Common.UI.Web.dll.config...

For whatever reason

$(TargetFileName)

is taking on the name of the .csproj file name. If I replace it with just log4net, then it outputs correctly

Update

File is stuck in obj folder and is not getting picked up when publishing.

Answer

Larry Dukek picture Larry Dukek · May 14, 2011

UPDATED: For Visual Studio 2012 (these updates work in VS2010 also)

After attempting many different solutions I dug into how web.Config transformations happen... Here is what I find the most elegant solution.

First off exclude your log4net.config files from your project, unless you truly understand the project XML you might end up with a very confusing duplicate reference. Do NOT delete the files just exclude them (we will be including them through the project editor).

Now unload your project and then edit it... or however you choose to get to the proj xml. Ensure that you have a node importing Microsoft.WebApplication.targets. If you are in a web project it might have been added for you... search for this node

<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />

Once you have this node you only need to add an ItemGroup node...

<ItemGroup>
  <WebConfigsToTransform Include="log4net.config">
    <DestinationRelativePath>log4net.config</DestinationRelativePath>
    <Exclude>False</Exclude>
    <TransformFileFolder>$(TransformWebConfigIntermediateLocation)\original</TransformFileFolder>
    <TransformFile>log4net.$(Configuration).config</TransformFile>
    <TransformOriginalFolder>$(TransformWebConfigIntermediateLocation)\original</TransformOriginalFolder>
    <TransformOriginalFile>$(TransformWebConfigIntermediateLocation)\original\%(DestinationRelativePath)</TransformOriginalFile>
    <TransformOutputFile>$(TransformWebConfigIntermediateLocation)\transformed\%(DestinationRelativePath)</TransformOutputFile>
    <TransformScope>$(_PackageTempDir)\%(DestinationRelativePath)</TransformScope>
    <SubType>Designer</SubType>
  </WebConfigsToTransform>
  <None Include="log4net.Debug.config">
    <DependentUpon>log4net.config</DependentUpon>
  </None>
  <None Include="log4net.Release.config">
    <DependentUpon>log4net.config</DependentUpon>
  </None>
</ItemGroup>

I have included the dependent files with in the ItemGroup, although this is not necessary, but it keeps things together. Notice that you didn't create a new task or target, now the transformation is handled EXACTLY like the web.config transformations.