This is part two of a two part article. In the first part I reported on a couple of findings when working with the SPWebConfigModification API, in the this part I will introduce a simulator tool that allows to learn, experiment and generate code for SPWebConfigModification related tasks.

The utility can be downloaded from codeplex, it can be found here: http://wcms.codeplex.com.

This utility is applicable for SharePoint 2007 and SharePoint 2010. It has been compiled with a reference to the SharePoint 2010 assembly (hence the .NET 3.5 framework requirement), by changing the SharePoint version in the options screen a binding redirect is added in the configuration file so we can make it work with SharePoint 2007. Changing this setting requires a restart of the application.

WebConfigModificationSimulator – Some Features

  • Work with SPWebConfigModification without changing the configuration database or Web.Config – (avoid corrupt property bags, duplicate entries, …)
  • Work with actual artifacts (SharePoint Web.config, SPWebConfigFileChanges property bag entries) – (needs SharePoint Permissions, read-only)
  • Work on empty xml (or xml of own choice) to have clearer view.
  • Drag and Drop of entries between the two entry lists.
  • Auto lookup and complete when entering modification paths into the Web.config (avoids mistakes or typos).
  • Wizard like entry form that allows to easily create SPWebConfigModification entries, the system composes the name and value properties.
  • Code generation with possibility to save either in VB.NET or C#.
  • Save list entries to disk for later use.

WebConfigModificationSimulator – Introduction

This utility lets you experiment with SPWebConfigModification without the risk of messing up the content configuration database or the Web.config file. The main UI looks like this:

MainUI

When the window is loaded the utility tries to get a list of SPWebApplication instances in the local farm. If the user has not the necessary privileges to perform this action the text part of the web.config dropdown will show the message “Error – Could not load SharePoint WebApplications.“. The utility is fully functional but no live information can be retrieved from SharePoint. In this case you can add entries yourself (and maybe save them to file for later retrieval).

Adding SPWebConfigModification entries is done in the left part of the window, when these modifications are ‘applied’ the config file on the right will be updated. You can see that there are two lists where you can enter new SPWebConfigModification entries. This is to simulate the entries stored in the SPWebConfigFileChanges and SPWebApplication property bag (hence the Database icon) and the ones added or removed by code. You do not have to use the first list to experiment with SPWebConfigModification entries, but using the two will provide a more realistic view on what SharePoint is doing behind the scenes.

On the right part of the window you can see a Code tab, this is where you can generate code for the SPWebConfigModification entries in the two lists, this will be explained further in this article.

The entry area below the second list allows you to add new entries or modify existing ones. The name and value properties have to be added as they are written to the SharePoint system.

The utility provides a way to abstract this by using a kind of wizard entry form. This makes it really easy to add new SPWebConfigModification entries, no guessing anymore what needs to be entered. This will be shown in the next topic.

This code works exactly as the code SharePoint executes behind the scenes. I used reflector to extract the relevant parts, I mocked up some internal classes and the whole is an exact simulation of what happens when you work with SPWebConfigModification entries.

WebConfigModificationSimulator – Add SPWebConfigModification

Let’s do a first test with the simulator. We want to start simple and add a new node to the Web.config. Let’s call the node myFeatureConfig. It will be a node under our control, no one else should add nodes to it outside the scope of the feature itself so it is going to be an EnsureChildNode entry.

Start by clicking on the green plus icon (Add) in the header of the second list (or use the context menu on the second list), a new window will open:

AddEntry

The default Modification type is set to EnsureChildNode. We leave it as it is.

I already started typing in the Modification path textbox and you can see that I implemented auto lookup and auto complete. Since the node I want to add is directly under the configuration node I can just press Tab.

In the Node name textbox I type myFeatureConfig.

I then have the option to enter one or more attributes. Let’s just add one for fun, call it level and give it a value of 5.

Optionally you can enter then an xml fragment that is the innerXml of the new node. We do not have a need for that now so let’s leave it empty. The window should look now like:

AddEntry1

Notice that the name and value textbox contain automatically the correct value. In this way we are sure that these values are in sync.

Click OK.

This is the window as it will look like:

MainUI1

Now simulate ApplyWebConfigModifications of by clicking on the Apply Modifications icon (Apply). now the window looks like this:

MainUI2

The SPWebConfigModification entry has been applied and can be seen in the config file on the right. The entry is also shown in the first list now (which simulates that it has been stored in the SPWebConfigFileChanges and SPWebApplication property bags.

Remark:

A small note on the use of the checkboxes.

What I am trying to do here is simulate the process that is performed by custom code on one side (adding/removing SPWebConfigModification instances) and by SharePoint (the ApplyWebConfigModifications internal code). I gave it some thought but did not find an easy visual metaphor to represent this whole process. So enter the checkboxes…

The checkbox on the entries in the first list indicate whether we use either only the entry from the SPWebConfigFileChanges property bag (unchecked) or both from the SPWebConfigFileChanges and SPWebApplication property bags (checked). I use this to simulate the removal of SPWebConfigModification entries via code. Unchecking the checkbox in the previous scenario would be equal to:

SPWebService webService = SPWebService.ContentService;
SPWebApplication webApplication = webService.WebApplications[“SharePoint – 80”];
Collection<SPWebConfigModification> webConfigModifications = webApplication.WebConfigModifications;

webConfigModifications.Remove(webConfigModifications[0]);

///……

Once an entry exist in the first list a link becomes available in the first item of the second list (details). If you click on this link a new window will open that details what will be executed during the simulation, suppose we click on it when the first item in the first list is checked, we will see the following:

Details

This window shows indeed that the selected node will be first removed (as entry in the SPWebConfigFileChanges property bag) and then re-added as entry in the SPWebApplication property bag)

If the entry would be unchecked we would see:

Details2

The checkbox on the entries in the second list indicate only whether we ignore the selected entry from the ApplyWebConfigModifications process.

The utility has following context menu on the two lists:

ContextMenu

  • Get from ‘xxxx’: this menu item tries to retrieve the entries of the SPWebConfigFileChanges property bag. If the user has no access this option is not available. This menu item is only available on the first list.
  • Add WebConfigModification: shows the wizard entry form.
  • Delete WebConfigModification: deletes the currently selected entry from the list.
  • Load WebConfigModifications: loads the entries from a file into the selected list.
  • Save WebConfigModifications: saves the entries from the selected list to a file.
  • Apply WebConfigModifications: simulates the ApplyWebConfigModifications.
  • Clear All: removes all the entries from the selected list.

WebConfigModificationSimulator – Remove SPWebConfigModification

Now let’s continue with the previous scenario and simulate the removal of the SPWebConfigModification. When you remove a SPWebConfigModification entry through code you actually change the WebConfigModifications collection from either an SPWebApplication or SPWebService instance after which you call Update. This updates modifies actually the property bags in the configuration database. When you then call ApplyWebConfigModifications SharePoint uses these property bags to initiate the process.

This is what you can simulate by checking or unchecking the checkboxes in the first list. If you want to simulate the removal of a SPWebConfigModification entry all you have to do is uncheck the checkbox of that entry in the first list (remember this list represents the property bags in the configuration database)

Let’s do this in our example, uncheck the checkbox in the first list and either uncheck the checkbox in the second list or remove the entry by deleting it (if you leave this entry you simulate the addition of a new SPWebConfigModification entry and this is not what we try to do). This is what you should have now:

MainUI3

As you can see the entry has been removed from the Web.config file.

InfoThis utility allows you to verify whether the SPWebConfigModification properties are balanced in such a way that adding and removing nodes works correctly without having to modify a live environment.

WebConfigModificationSimulator – Code generation

The utility also allows to generate code for the entries currently defined. There is a choice between VB.NET and C#. Go to the Code tab, select you programming language and click on Generate. The utility will generate code using a preprocessed T4 template (new feature in Visual Studio 2010).

MainUI4

Here you see some auto generated code in C# for adding and removing SPWebConfigModification entries:

using System;
using System.Collections.ObjectModel;
using Microsoft.SharePoint.Administration;

namespace Wilke.SharePoint.Tools
{
public static class WebConfigModificationsHelper
{
private static SPWebService contentService = null;
private static SPWebApplication webApplication = null;

public static bool ApplyWebConfigModifications(string webAppUri)
{
bool result = false;

try
{
contentService = SPWebService.ContentService;
webApplication = SPWebApplication.Lookup(new Uri(webAppUri));

AddWebConfigModifications();
}
catch (Exception exception)
{
//TODO
//Provide appropriate exception handling.
}

return result;
}

private static bool AddWebConfigModifications()
{
bool result = false;

try
{
SPWebConfigModification spWebConfigModification = null;
spWebConfigModification = new SPWebConfigModification();
spWebConfigModification.Owner = “[WebConfigModificationTester]”;
spWebConfigModification.Path = “configuration”;
spWebConfigModification.Name = “myFeatureConfiguration[@level=’5′]”;
spWebConfigModification.Value = “<myFeatureConfiguration level=’5’/>”;
spWebConfigModification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
spWebConfigModification.Sequence = 0;
webApplication.WebConfigModifications.Add(spWebConfigModification);

spWebConfigModification = new SPWebConfigModification();
spWebConfigModification.Owner = “[WebConfigModificationTester]”;
spWebConfigModification.Path = “configuration/myFeatureConfiguration[@level=’5′]”;
spWebConfigModification.Name = “useDatabase”;
spWebConfigModification.Value = “<useDatabase/>”;
spWebConfigModification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
spWebConfigModification.Sequence = 0;
webApplication.WebConfigModifications.Add(spWebConfigModification);

webApplication.Update();
contentService.ApplyWebConfigModifications();
}
catch
{
throw;
}

return result;
}

private static bool RemoveWebConfigModificationsInternal(string owner, string path, string name)
{
bool result = false;
bool needsUpdate = false;

try
{
Collection<SPWebConfigModification> webConfigModifications = webApplication.WebConfigModifications;

int modificationCount = webConfigModifications.Count;
for (int index = modificationCount – 1; index > -1; index–)
{
if (webConfigModifications[index].Owner == owner &&
webConfigModifications[index].Path == path &&
webConfigModifications[index].Name == name)
{
webConfigModifications.Remove(webConfigModifications[index]);
needsUpdate = true;
}
}
if (needsUpdate)
{
webApplication.Update();
contentService.ApplyWebConfigModifications();
}
}
catch
{
throw;
}

return result;
}

private static bool RemoveWebConfigModificationsInternal(string owner)
{
bool result = false;
bool needsUpdate = false;

try
{
Collection<SPWebConfigModification> webConfigModifications = webApplication.WebConfigModifications;

int modificationCount = webConfigModifications.Count;
for (int index = modificationCount – 1; index > -1; index–)
{
if (webConfigModifications[index].Owner == owner)
{
webConfigModifications.Remove(webConfigModifications[index]);
needsUpdate = true;
}
}
if (needsUpdate)
{
webApplication.Update();
contentService.ApplyWebConfigModifications();
}
}
catch
{
throw;
}

return result;
}
}
}

Remarks:

  • Namespace and SPWebConfigModification owner can be changed via the options.
  • In this first version the T4 template is already preprocessed, in a future version I might make it as a configurable feature.

WebConfigModificationSimulator – Options

Options

  • SharePoint version: The version of the Microsoft.SharePoint assembly to use, changing this requires a restart of the application.
  • Path to SharePoint config files: the path where SharePoint stores its configuration files. Normally this inside the wss folder in the C:inetpub folder but this can be also in a different location.
  • Namespace for code generation: the namespace used when the code is generated.
  • Default modification owner: the owner set when creating SPWebConfigModification instances.
  • Frequently used node names: a list of node names frequently used in config files. This list is used for auto lookup in the node name textbox.
  • Nodes to ignore in auto lookup: SharePoint config files contain lots of entries that are not suitable for serving as parent node (e.g. SafeControls entries). During the collection of nodes to present in the auto lookup these nodes will not be included.
  • Auto lookup differentiating attributes: Some parent nodes have one or more attributes, when the node is selected as path we need to be able to uniquely identify this node. For this we use a differentiating attribute. This list represent attribute names that are frequently used to differentiate parent nodes with the same tag name.

WebConfigModificationSimulator – Known limitations

The auto lookup feature tries to enumerate the available nodes in the current config file (the one in the right part of the window). In order to identify nodes that contain attributes uniquely the utility uses following approach:

  • If the attribute has one of the following names: name, path or id, it will be used as differentiator of the node.
  • If the condition above is not met the first attribute is selected as differentiator.

This means that it is possible that a node with attributes is not found in the dropdown. In that case you can just type the path in the textbox.The options screen allows you to add additional attribute names tat should be recognized during the collection of the nodes to add for auto lookup.

WebConfigModificationSimulator – Acknowledgements

The code generation feature makes use of the ColorCode.dll. This library provides syntax coloring for several programming languages. The codeplex project can be found here: http://colorcode.codeplex.com/.

In order to keep a single executable package I used ILMERGE to merge the primary executable with the ColorCode.dll into one new merged executable. Using ILMERGE-GUI did help me not having to remember the command line arguments for ILMERGE. The codeplex project can be found here: http://ilmergegui.codeplex.com/.

WebConfigModificationSimulator – Feedback

This is the first time I publish a utility for a broader audience so I’m still in the process of fine tuning this process. If you have some constructive feedback please leave me a message, this might be about the articles on the utility or the utility itself.