Thursday, 17 July 2008

Master the order of WebConfigModifications

There are plenty of blogs describing WebConfigModifications so I'm just going to stress what I feel are the two most important properties on the WebConfigModifications class.

The name and the path.

There are plenty of descriptions of these properties but the most important thing to note is that when combined they form the xpath used to locate the node on which to operate.

Therefore, the following 2 examples have the same effect.



This is because when the name and path attributes are combined they result in the same xpath.

configuration/system.web/httpHandlers/add[@verb='*' and @path='*.asmx']

The reason I am stressing this so much is that I've noticed lots of complaints about how the sequence attribute does not appear to affect the order in which the modifications are applied. This is because the sequence attribute is only taken into account when the names of the modfications are the same. This isn't much use. This means that if I try to insert the following lines into the web.config

<clear />
<add verb="*" path="*.asmx" />
it would always appear

<add verb="*" path="*.asmx" />
<clear />
because alphabetically the names are

add[verb='*' and path='*.asmx']
clear
Because the names are different the sequence would be ignored and the modifications would be applied alphabetically. Therefore adds would come before clears and removes would be last.

That sucks!! But help is at hand. Now that we now the name attribute just forms part of the xpath to locate the element in the web.config file we can use this to our advantage.

The earlier examples formed the xpath

configuration/system.web/httpHandlers/add[@verb='*' and @path='*.asmx']

Well lets alter this example (or will we). If we add a condition to httpHandlers but make sure it always equates to true, the xpath would return the same node but the name used in the WebConfigModification construct would be slightly different.

configuration/system.web/httpHandlers[1=1]/add[@verb='*' and @path='*.asmx']

The condition always returns true and will be ignored but lets look at how this changes the code.



The names are now (in alphabetic order)

httpHandlers[1=1]/clear
httpHandlers[2=2]/add[@verb='*' and @path='*.asmx']

and a therefore will be applied as wanted/expected.

<clear />
<add verb="*" path="*.asmx" />

8 comments:

hughes said...

Using this technique, the web.config modifications are stored in the correct order; however, there’s an issue when trying to remove the modifications. The modifications are removed from the database when using SPWebConfigModification.Remove(), but the web.config is not updated. Have you experienced this issue?

msvinth said...

For some reason this did not work for me. I had to still leave the path correct otherwise the new element would not a child of the right parent. To get it to work I changed the name to go to the parent and then to the element it should be under. Like:

name="parent::httpHandlers[1=1]/remove[@path='*.asmx']"

bksmoove said...

Great post. But the xpath value in your code didn't work for me. I had to change it to:

SPWebConfigModification modification = new SPWebConfigModification(name, "configuration/system.web/httpHandlers");

Anonymous said...

Have you been able to get this to actually work? I've tried it with EnsureChildNode and it does not place the elements in the expected place.

Stephen K said...

I can tell you that I never post anything that I haven't implemented with one exception (Execute entire page with elevated privileges).

What I'm highlighting here is the use of a contidition that always equates to true to facilitate ordering of the WebConfigModifications. The exact xpath needed will vary depending on what you are trying to acheive.

Anonymous said...

name="parent::httpHandlers[1=1]/remove[@path='*.asmx']"

this works not correct for remove and not check for present in the web.config.

Change to:
../httpHandlers[1=1]/remove[@path='*.asmx']"

and works fine.

Bjørn Stærk said...

Nice tip! I tried this on SharePoint 2010, and I had to move the [1=1] part into the path, so you get for instance:
Path = configuration/system.webServer/modules[1=1]
Name = remove[@name='AnonymousIdentification']

Otherwise the node was added one level above.

Bill Armstrong said...

Dude,

Your workaround is nothing short of brilliant, and it saved my bacon. You need to update your code sample though, because as others have already pointed out, your sample does not work as you intended. Here is why:

The Name property is used to determine if the element exists at the node specified by the Path proeprty. If the node does not exist, then the XML that you provide with the Value property will get appended to the Path node. With your code sample that means that both the clear and add elements will get added to "configuration/system.web", but you actually want them at "configuration/system.web/httpHandlers".

To get the result that you intended, you need to set the properties like so:

modification.Path = "configuration/system.web/httpHandlers";
modification.Name = "../httpHandlers[1=1]/clear"

With the properties set like that, it will still order the modifications as you intended, but the XML in the Value property will get added to the correct node.

I know that you said that you never post anything that you haven't implemented, but as I explained, the code that you wrote will clearly not do what you intended.

The scenario that I was trying to implement is pretty much the same as yours, and when I tried to implement the code as you wrote it, the XML element got added to the wrong node. It got added to the node specified by the Path property, which makes sense.

It wasn't until I read the comments below your post that I was able to get it working.