Introduction

This is part one of a two part article. In the first part I will report on a couple of findings when working with the SPWebConfigModification API, in the second part I will introduce a simulator tool that allows to learn, experiment and generate code for SPWebConfigModification related tasks. This topic is not in any way a replacement for the official documentation, it only serves to highlight a couple tricky aspects.

Abstract

The SharePoint SPWebConfigModification API allows the developer to write code that updates the web.config in a semi transactional way. By semi transactional I mean that changes made to the web.config can be rolled back when a certain condition is met. SharePoint will also take care of making the changes to all web front ends in the SharePoint farm. The uneasy part of working with this API is that it is not that transparent to use and is not that well documented (although for the SharePoint 2010 version this seems to be somewhat better).

The basic steps for modifying the web.config are:

  • Get the current list of modifications
  • Remove existing entries if required
  • Add new entries if required
  • Update modifications
  • Apply modifications

As you can see these basic steps are easy to follow but in real life they can lead sometimes to unexpected situations.

Closer Look

Let’s go over the basic steps and look at these steps from a SharePoint point of view.

Get the current list of modifications

You can store SPWebConfigModification entries on the level of a SPWebService or SPWebApplication. In the former case the changes will be applied to all SPWebApplication instances in the farm, in the latter case the changes will be applied only to the selected SPWebApplication.

Where does SharePoint store the SPWebConfigModification entries for a SPWebService or SPWebApplication? Short answer: in the SharePoint configuration database. Longer answer: in two property bags that are persisted in the ‘Objects’ table in the SharePoint configuration database.

There are indeed two property bags where these modifications are stored, both in the Object table in the farm’s configuration database.

Let’s give an example:

Clean Situation

Suppose I have a farm with one SPWebApplication instance, and no SPWebConfigModification entries have been added. This is the initial clean situation.

I will look up information in the Object table with following SQL statement:

SELECT ParentId, ClassId,Name, CONVERT(xml, Properties) AS Properties
FROM [SharePoint2010_Config].[dbo].[Objects]
WHERE Name LIKE ‘%WebConfigChanges%’

Starting from the initial situation I would get:

tcz3kayz

Why did I use ‘%WebConfigChanges%’ in the query, because this is how SharePoint identifies the property bag that contains changes applied to the web.config.

Some remarks about the resultset:

- I did not print out the full content of the Properties field as this would be too long. But by using CONVERT(xml, Properties) I can click on the link in the properties field and a new window will open with the xml. This avoids you of having to go through the field as text (which is also limited in size).

The default xml looks as follows:

<object type=Microsoft.SharePoint.Administration.SPWebConfigFileChanges, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c>
  <fld type=System.Collections.SortedList, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 name=WebConfigChildNodes />
  <fld type=System.Collections.SortedList, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 name=WebConfigAttrChanges />
  <fld type=System.Collections.SortedList, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 name=WebConfigSections />
  <fld type=System.Collections.Hashtable, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 name=m_UpgradedPersistedFields />
  <fld name=m_Properties type=null />
  <sFld type=String name=m_LastUpdatedUser>WKS-HERMES\Wilke</sFld>
  <sFld type=String name=m_LastUpdatedProcess>psconfigui (3024)</sFld>
  <sFld type=String name=m_LastUpdatedMachine>WKS-HERMES</sFld>
  <sFld type=DateTime name=m_LastUpdatedTime>2010-08-25T20:59:06</sFld>
</object>

- The name WebConfigChanges – <Guid> is created by the API as follows:

internal class SPWebConfigFileChanges : SPPersistedObject
        {
            private static string s_BaseName;
            static SPWebConfigFileChanges()
            {
                s_BaseName = “WebConfigChanges – “;
            }
            ///
            ///
            ///
            internal static string GenerateName(Guid serverId)
            {
                return (s_BaseName + serverId.ToString());
            }
            ///
            ///
            ///
        }

So the Guid is actually the id of the SharePoint server (farm).

- The ClassId is an identifier that tells you what class this property bag is associated with, in this case the ClassId points to the SPWebConfigFileChanges class in the Microsoft.SharePoint.Administration namespace, this can be seen also with reflector, look at the Guid attribute on this class definition:

[Obfuscation(Exclude = true, Feature = "renaming", StripAfterObfuscation = true), Guid("9B0E7DE4-847D-42cf-A84F-99CD76D4F1E6")]
internal class SPWebConfigFileChanges : SPPersistedObject
{
   /// <summary>
   ///
   /// </summary>
}

Why are there two property bags although I only have one SPWebApplication instance? The second property bag comes from the Central Administration SPWebApplication. This can be traced back by taking the ParentId and executing a query to retrieve the property bag associated with that Id and then looking at the ClassId.

This is a list of some common ClassId’s

SPWebService            45AD2BF2-4E3E-46A1-B477-126944C0ACEF
SPWebApplication        113FB569-7520-4651-8FC4-E9F4F5887618
SPAdministrationWebApplication    4C0FA7BC-0812-4ED2-80AB-89D752898BC6
SPWebConfigFileChanges        9B0E7DE4-847D-42CF-A84F-99CD76D4F1E6
 
Apply one SPWebConfigModification

Let’s look at the situation where we apply one SPWebConfigModification to the SPWebApplication instance. This is done as follows:

SPWebService contentService = SPWebService.ContentService;
SPWebApplication webApplication = SPWebApplication.Lookup(new Uri(“SharePoint – 80″));

SPWebConfigModification spWebConfigModification = null;
spWebConfigModification = new SPWebConfigModification();
spWebConfigModification.Owner = “[WebConfigModificationTester]“;
spWebConfigModification.Path = “configuration”;
spWebConfigModification.Name = “connectionStrings”;
spWebConfigModification.Value = “<connectionStrings/>”;
spWebConfigModification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
spWebConfigModification.Sequence = 0;
webApplication.WebConfigModifications.Add(spWebConfigModification);

webApplication.Update();
contentService.ApplyWebConfigModifications();

Now execute our query again but this time change the WHERE clause a bit:

SELECT ParentId, ClassId,Name, CONVERT(xml, Properties) AS Properties
FROM [SharePoint2010_Config].[dbo].[Objects]
WHERE Properties LIKE ‘%WebConfigModificationTester%’

This is because as you can see from the code snippet that adds the SPWebConfigModiciation we have set the Owner to [WebConfigModificationTester].

This is the new resultset:

qdpjmvwu

We have two rows in the resultset:

The first row is the property bag of the SPWebApplication (verify ClassId in ClassId table)

The second row is the updated property bag of the SPWebConfigFileChanges instance, the xml looks as follows:

<object type=Microsoft.SharePoint.Administration.SPWebConfigFileChanges, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c>
  <fld type=System.Collections.SortedList, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 name=WebConfigChildNodes>
    <sFld type=String>configuration/connectionStrings</sFld>
    <fld type=Microsoft.SharePoint.Administration.SPWebConfigModification, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c>
      <object type=Microsoft.SharePoint.Administration.SPWebConfigModification, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c>
        <sFld type=String name=m_Name>connectionStrings</sFld>
        <sFld type=UInt32 name=m_Sequence>0</sFld>
        <sFld type=String name=m_XPath>configuration</sFld>
        <sFld type=String name=m_Owner>[WebConfigModificationTester]</sFld>
        <sFld type=String name=m_Value>&lt;connectionStrings/&gt;</sFld>
        <fld type=Microsoft.SharePoint.Administration.SPWebConfigModification+SPWebConfigModificationType, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c name=m_Type>EnsureChildNode</fld>
        <sFld type=String name=m_PreviousAttrValue />
      </object>
    </fld>
  </fld>
  <fld type=System.Collections.SortedList, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 name=WebConfigAttrChanges />
  <fld type=System.Collections.SortedList, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 name=WebConfigSections />
  <fld type=System.Collections.Hashtable, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 name=m_UpgradedPersistedFields />
  <fld name=m_Properties type=null />
  <sFld type=String name=m_LastUpdatedUser>WKS-HERMES\Wilke</sFld>
  <sFld type=String name=m_LastUpdatedProcess>ConsoleApplication2.vshost (10408)</sFld>
  <sFld type=String name=m_LastUpdatedMachine>WKS-HERMES</sFld>
  <sFld type=DateTime name=m_LastUpdatedTime>2010-11-09T15:12:52</sFld>
</object>

Here you see that a new fld element is added with the information added by the SPWebConfigModification entry.

Why is the SPWebConfigModification stored in two places?

The entry in the property bag associated with the SPWebApplication is coming from the statement:

webApplication.Update();

The entry in the property bag associated with the SPWebConfigFileChanges is coming from the statement:

contentService.ApplyWebConfigModifications();

So this means if you only call the first statement no change will be made at that time to the actual web.config file. The entry is only stored in the property bag of the SPWebApplication instance. If at a later stage the ApplyWebConfigModifications is called the changes will be stored in the SPWebConfigFileChanges property bag and propagated to the actual Web.config file.

If you do not call the Update statement strange things can happen, the first time you call ApplyWebConfigModifications the entry will be stored in the SPWebConfigFileChanges property bag and the entry will be added to the Web.config, nothing will be stored in the SPWebApplication property bag though. If you call a second time ApplyWebConfigModifications (without invoking the code that modifies the WebConfigModifications collection) you will notice that the entry will be removed from the SPWebConfigFileChanges property bag and also removed from the Web.Config. An explanation can be found when we look how SharePoint processes the SPWebConfigModification entries.

This means that:

  • Update saves the entries to the property bag associated with SPWebApplication or SPWebService.
  • ApplyWebConfigModifications stores the entries to the property bag associated with the SPWebConfigFileChanges instance and makes the changes to the Web.config.

How does SharePoint apply SPWebConfigModifications?

We have seen that changes to the actual Web.config file are made by the call to ApplyWebConfigModifications. So let’s focus on this method.

Each time you call ApplyWebConfigModifications there are a couple of steps that are performed:

ApplyWebConfigModification

Let’s go over the scenario again where ApplyWebConfigModifications was called without a prior Update in the case where only one SPWebConfigModification is added to the WebConfigModifications collection of the  SPWebApplication instance.

If you look at this process you can now understand why the Web.Config change is removed the second time you call ApplyWebConfigModifications without having called Update. The first time you invoke the code, the entry is found in the WebConfigModifications collection of the SPWebApplication instance you are working with and that you changed via code (although it has not yet been updated it is available in memory). So these changes will be applied to the Web.config file and stored in the SPWebConfigFileChanges property bag.

The next time you call ApplyWebConfigModifications (if you did not invoke the code that makes a change to the WebConfigModifications collection) the entry will be removed from the Web.config file (assuming the entry can be found and it is not an EnsureSection entry) and since it is not to be found in the SPWebApplication property bag (either from config database and/or via code) it will not be reapplied. Since at this time the new entries collection is empty an the SPWebConfigFileChanges property bag will not contain any entries anymore.

What’s the difference between EnsureSection and EnsureChildNode

Conceptually EnsureSection is used when you want to be sure that a parent node to which additional nodes have to be added exist. Since in some cases you are not the only one that will add nodes to this parent node it can not be removed. A good example is the connectionStrings node, if you need to add a connectionString as part of a feature deployment you must be sure that the parent node in this case connectionStrings exists. But once the node has been added it is very well possible that other developers have added connectionString nodes to this parent node. If the feature you created is deactivated (assuming the SPWebConfigModification logic is in the feature event receivers) the parent node can not be removed without further complications for other parts of environment.

So EnsureSection should be used for single nodes, only the Path and Name property of the SPWebConfigModification are used.

EnsureChildNode is used when:

  • You need to add a single node, e.g. add connection string entry to connectionStrings tag. This node is under your control and should be able to be removed again.
  • You need to add a xml fragment to a parent node.
Adding and EnsureChildNode entry

When an EnsureChildNode entry is added: Path, Name and Value properties are used. Process flow for modifying the Web.config file:

EnsureChildNode-Add

 WarningImportant to note is that SharePoint does not validate whether the Name property can be used to remove the node added by the Value property.
 
Removing and EnsureChildNode entry

When an EnsureChildNode entry is removed: Path and Name properties are used. Process flow for modifying the Web.config file:

EnsureChildNode-Remove

So here you see that the Name and Value property must be aligned, if not it is possible that you can add an EnsureChildNode entry but never remove it again, or you add multiple times the same node.

In the following article I will introduce an small utility that will allow developers to learn, experiment and generate code for working with SPWebConfigModification entries without writing to the configuration database or changing the Web.config file. It allows you also to get the actual entries stored in the different property bags to provide a more life like experience.

Comments are closed.