55

I would like to apply a transformation if and only if a matched element does not exist in the target. Trying various xpath expressions using http://webconfigtransformationtester.apphb.com/ but no luck so far.

E.g. if the target web.config looks like this:

<configuration>
  <system.web>
    <compilation debug="true" />
  </system.web>
</configuration>

then the output should look like this:

<configuration>
  <connectionStrings>
    <add name="MyCs" provider="System.Data.SqlClient" connectionString="" />
    <add name="SomeOtherCs" provider="System.Data.SqlClient" connectionString="" />
  </connectionStrings>
  <system.web>
    <compilation debug="true" />
  </system.web>
</configuration>

But if the target looks like this:

<configuration>
  <connectionStrings>
    <add name="MyCs" provider="System.Data.IChangedIt" connectionString="my connection string here" />
  </connectionStrings>
  <system.web>
    <compilation debug="true" />
  </system.web>
</configuration>

then the result of the transformation should look like this:

<configuration>
  <connectionStrings>
    <add name="MyCs" provider="System.Data.IChangedIt" connectionString="my connection string here" />
    <add name="SomeOtherCs" provider="System.Data.SqlClient" connectionString="" />   
  </connectionStrings>
  <system.web>
    <compilation debug="true" />
  </system.web>
</configuration>

In other words, I just want to add the named connection string to configuration but let the administrator fill it in with his own values. I thought it would as simple as xdt:Transform="Insert" xdt:Locator="XPath(count(/configuration/connectionStrings)=0)" (to add a cs config section if none existed) but apparently not.

João Bragança
  • 1,353
  • 1
  • 13
  • 29
  • have you tried `xdt:Locator="XPath(/configuration[not(connectionStrings)])"` ? – collapsar Mar 03 '13 at 00:21
  • possible duplicate of [Is there any way to do a "Replace Or Insert" using web.config transformation?](http://stackoverflow.com/questions/5732681/is-there-any-way-to-do-a-replace-or-insert-using-web-config-transformation) – p.s.w.g Jul 23 '15 at 18:17

5 Answers5

69

Use xdt:Transform="InsertIfMissing" with the XmlTransform task in VS2012. It doesn't look like Microsoft has updated their documentation to reflect this yet.

Pure.Krome
  • 84,693
  • 113
  • 396
  • 647
ADW334034
  • 2,508
  • 1
  • 16
  • 5
47

In my case xdt:Transform="InsertIfMissing" did not work without xdt:Locator="Match(name)"

GerardBeckerleg
  • 881
  • 1
  • 8
  • 14
  • 1
    It may be that this is only necessary on leaf nodes – Chris F Carroll Jul 17 '17 at 11:02
  • This was what I needed. I was trying to insert a `location` tag for a specific path, however the original web.config already had some `location` tags for other paths. So I just had to add a `xdt:Locator="Match(path)` tag. I know it looks odd, but in my case I couldn't let this specific location in the original config, only when publishing to servers. – Alisson Reinaldo Silva Feb 14 '18 at 10:37
  • This was exactly what I needed - add keys but don't modify if they exist. I needed the transform, locator and the value. This is with VS2015 – Reinstate Monica Cellio Mar 07 '18 at 17:04
  • +1 This was the key and in my setting my xdt:Locator="Match(key)" for anyone copy pasting and not seeing what to "replace". VS2017 for me. – kenny Feb 25 '19 at 18:27
12

Try this alternative transformation for xdt:Transform="InsertIfMissing" :

<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <nodeToInsertIfMissing xdt:Transform="Insert" />
  <nodeToInsertIfMissing xdt:Transform="Remove" xdt:Locator="XPath(/configuration/nodeToInsertIfMissing[2])" />
</configuration>

It should works following MSDN documentation:

Insert - adds the element that is defined in the transform file as a sibling to the selected element or elements. The new element is added at the end of any collection.

So, if the node already exists, we add the second one and then remove this node (2nd). Otherwise, we add the new, unique node but remove operation will fail.

Note: It seems not working with NuGet *.(un)install.xdt transformation. InsertIfMissing too.

Kryszal
  • 1,663
  • 14
  • 20
5

Confirmed working in VS2015 and Package Manager Console Host Version 3.4.4.1321 (you can find this when you open the Package Manager Console).

This will insert if 'configuration\connectionStrings\add\@name' does not exist.

The app.config.install.xdt:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
    <connectionStrings xdt:Transform="InsertIfMissing">
        <add name="MyCs" provider="System.Data.SqlClient" connectionString="" xdt:Transform="InsertIfMissing" xdt:Locator="Match(name)"/>
    </connectionStrings>
</configuration>

The .nuspec file:

<files>
    <file src="app.config.install.xdt" target="content\app.config.install.xdt" />
OzBob
  • 4,227
  • 1
  • 39
  • 48
  • 1
    Possibly worth noting - I found that this failed if the app.config didn't already have a connectionStrings element in it, you can add xdt:Transform="InsertIfMissing" to that too though to cover this case. – James Morcom Sep 12 '17 at 11:25
4

Use xdt:Transform="Remove" followed by xdt:Transform="Insert" transforms. The xdt:Transform="InsertIfMissing" suggested elsewhere did not work for me, looks like its version specific.

Taras Alenin
  • 8,094
  • 4
  • 40
  • 32