I'm using Microsoft's XDT library to transform my web.config files and discovered that Locator is not working as expected. Using the example below, I would expect that both the attributes are set and the converter node is inserted in all three appenders, but only the attributes are updated in all three. The converter node is only inserted into the first appender. How do I get it to insert into all three log4net appender nodes?
I've tried switching to XPath, but it only throws errors. A working example would be nice, because every example I've followed so far seems to fail with an error.
Test site: https://webconfigtransformationtester.apphb.com/
For example:
Web.config
<?xml version="1.0"?>
<configuration>
<log4net>
<appender name="App1">
<layout>
<conversionPattern value="foo"/>
</layout>
</appender>
<appender name="App2">
<layout>
<conversionPattern value="foo"/>
</layout>
</appender>
<appender name="App3">
<layout>
<conversionPattern value="foo"/>
</layout>
</appender>
</log4net>
</configuration>
Web.Debug.config
<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<log4net>
<appender xdt:Locator="Condition(@name='App1' or @name='App2' or @name='App3')">
<layout>
<conversionPattern value="bar" xdt:Transform="SetAttributes" />
<converter xdt:Transform="Insert">
<name value="Default" />
<type value="Common.DefaultConverter, Common" />
</converter>
</layout>
</appender>
</log4net>
</configuration>
Results:
<?xml version="1.0"?>
<configuration>
<log4net>
<appender name="App1">
<layout>
<conversionPattern value="bar" />
<converter><name value="Default" /><type value="Common.DefaultConverter, Common" /></converter></layout>
</appender>
<appender name="App2">
<layout>
<conversionPattern value="bar" />
</layout>
</appender>
<appender name="App3">
<layout>
<conversionPattern value="bar" />
</layout>
</appender>
</log4net>
</configuration>
It turns out that it was designed this way. Only attributes are applied to all target nodes. To work around the issue, download the code from here and add the following lines:
In XmlElementContext, add the property:
internal bool HasLocator
{
get
{
return this.LocatorAttribute != null || (this.parentContext != null && this.parentContext.HasLocator);
}
}
In XmlTransform modify this line, adding the call to HasLocator:
if (ApplyTransformToAllTargetNodes || context.HasLocator) {
This code will determine if a "Locator" has been set within the current context or any parent context (like in my example) and apply all transforms to all target nodes.