Introduction
Since SharePoint is based on ASP.NET technology it uses web.config files to store settings used by the WebApplication. There are times when this file may need to be modified to add to setting necessary for a particular feature, such as, database connection strings or web services. Although you could edit the web.config file manually, in an Enterprise environment with multiple servers and multiple WebApplications this would quickly become an unsupportable and cumbersome technique. To overcome this the SharePoint API includes the SPWebModification
class to apply modification to elements or attributes in the web.config.
Unfortunately very little has been written about how this class functions and how modifications are applied so I will explore how to use it and other techniques to make modifications to web.config files in SharePoint.
To demonstrate I'll use a simple Console application rather than create a SharePoint feature so it can be easily stepped through in the debugger to see what is happening. Although teh code written using SharePoint 2010 the techniques apply to 2007 also.
SPWebModification class
To registers changes to the web.config file for a Web Application you need to create an instance of Microsoft.SharePoint.Administration.SPWebModification
class which is found in the Microsoft.SharePoint.dll
assembly.
SPWebConfigModification EnsureChildNode = new SPWebConfigModification
{
Owner = "CodeProject",
Path = "configuration/appSettings",
Name = string.Format("add [@key='myAttribute'] [@value='{0}']", 1),
Value = string.Format("<add key='myAttribute' value='{0}' />", 1),
Sequence = 1,
Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode
};
The class has six public properties that we are concerned with. The Owner
property is used to identify who made the modification and is typically set to the name of the feature or solution making the modifications. Later, when removing modifications I'll show how to use it. The Path
property is an XPath expression used to find the element within the web.config
file. In this example the
modification is being made to the appSettings
section. The Name
property is an XPath expression that is used in conjunction with the path to form a key to a SortedList as I'll show later and to apply modifications. The Value
property is the value to be inserted, or removed, from the web.cofig file. The Sequence
property tells what order to apply the modification. The Type
property tell what type of modification is being made, either a child node, attribute or section as defined by the SPWebConfigModification.SPWebConfigModificationType
enumeration. I'll take a closer look at these properties when applying the modifications.
Modifications being made are added to the SPWebApplication
class using the WebConfigModifications
property which is, of course, a collection of SPWebConfigModification
s. Once all modifications have been added to the collection they are applied to the web.config file by calling the ApplyWebConfigModifications
method of the SPWebService
class.
SPWebService.ApplyWebConfigModifications
This method will check if a TimerService
instance is available, and if so, create a SPWebConfigJobDefinition
instance scheduled to run immediately. Otherwise it will iterate through all WebApplications within its scope and call the ApplyWebConfigModifications
method for that SPWebApplication
. The same process occurs in the Execute
method of the SPWebConfigJobDefinition
if created.
SPWebApplication.ApplyWebConfigModifications
The first thing that happens in this method is any previous changes that have been made to the web.config file are retrieved from the configuration database as an instance of a SPWebConfigFileChanges
class. This class has three SortedLists
, one for each of the types of modifications that can be made, WebConfigChildNodes, WebConfigAttrChanges and WebConfigSections. A second instance of a SPWebConfigFileChanges
class is created and initialized with the SPWebApplication
.
From here the RebuildListModifications
method of the SPWebConfigFileChanges
class is called with the SPWebConfigModifications
collection of the SPWebApplication
. This method in turn calls the AddListModifications
method with the SPWebConfigModifications
collection. Its here where the Path
and Name
properties of the SPWebConfigModification
object are combined and used as the key for the appropriate modifications collection. If the key is not already present in the collection it will be added. In the case of the EnsureChildNode
and EnsureAttribute
types if the SPWebConfigModification
matching the key is found in the collection the Sequence
properties will then be compared. If the Sequence
of the modification being added is greater than or equal to the existing modification it will replace the the existing modification.
foreach (SPWebConfigModification modification in changes)
{
string key = modification.Path + "/" + modification.Name;
SPWebConfigModification modification2 = null;
switch (modification.Type)
{
case SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode:
{
modification2 = (SPWebConfigModification) this.WebConfigChildNodes[key];
if (modification2 == null)
{
break;
}
if (modification.Sequence >= modification2.Sequence)
{
this.WebConfigChildNodes[key] = modification;
}
continue;
}
}
}
To illustrate, I'll create two SPWebConfigModification
objects and set the Sequence
properties to 1 and 2.
public static SPWebConfigModification ChildNode = new SPWebConfigModification
{
Path = "configuration/appSettings",
Name = string.Format("add [@key='myAttribute'] [@value='{0}']", 1),
Sequence = 1,
Owner = "CodeProject",
Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode,
Value = string.Format("<add key='myAttribute' value='{0}' />", 1)
};
public static SPWebConfigModification ChildNode2 = new SPWebConfigModification
{
Path = "configuration/appSettings",
Name = string.Format("add [@key='myAttribute'] [@value='{0}']", 1),
Sequence = 2,
Owner = "CodeProject",
Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode,
Value = string.Format("<add key='myAttribute' value='{0}' />", 2)
};
After applying these modifications only the second one will be added to the web.config file.
webApp.WebConfigModifications.Add(ChildNode);
webApp.WebConfigModifications.Add(ChildNode2);
webApp.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();
After the RebuildListModifications
method completes the AddListModifications
is again called, this time with the WebConfigModifications
collection of the SPWebApplications
parent, the SPWebSevice
.
After all of the modifications have been added to the SPWebConfigFileChanges
the web.config file will be located the SPIssSettings
of the SPWebApplication
and loaded into a XmlDocument
. Using the XmlDocument the previous ChildNode and Attribute modifications are removed via the RemoveModificationsWebConfigXmlDocument
method. Then ApplyModificationsWebConfigXmlDocument
is called to add the new modifications to the web.config file.
Both the RemoveModificationsWebConfigXmlDocument
and ApplyModificationsWebConfigXmlDocument
very simply use the Path
property of the SPWebConfigModification
object as the parameter for the SelectSingleNode
method. If found, the node is then either removed or in the case of EnsureChildNode
the Name
property is used in another SelectSingleNode
using that node and the InnerXml
is appended to with the Value
property. In the case of the EnsureAttribute
the SetAttribute
method for the node is called using the Value
property. For the EnsureSection
type the Name
property is used in SelectSingleNode
for the parent node and if it does not exist it is appended to the InnerXml
of the parent node.
foreach (SPWebConfigModification modification2 in this.WebConfigChildNodes.Values)
{
XmlNode node2 = xdWebConfig.SelectSingleNode(modification2.Path);
if (node2 == null)
{
throw new SPException(SPResource.GetString("WebConfigModNodeNotFound", new object[] { filepath, modification2.Path }));
}
if (node2.SelectSingleNode(modification2.Name) == null)
{
if (modification2.Value.Length == 0)
{
throw new SPException(SPResource.GetString("WebConfigEmptyChildNodeValue", new object[] { modification2.Name }));
}
node2.InnerXml = node2.InnerXml + modification2.Value;
}
}
foreach (SPWebConfigModification modification3 in this.WebConfigAttrChanges.Values)
{
XmlElement element = xdWebConfig.SelectSingleNode(modification3.Path) as XmlElement;
if (element == null)
{
throw new SPException(SPResource.GetString("WebConfigModNodeNotFound", new object[] { filepath, modification3.Path }));
}
if (element != null)
{
modification3.PreviousAttrValue = element.GetAttribute(modification3.Name);
element.SetAttribute(modification3.Name, modification3.Value);
}
}
Notice from this code that current value of the attribute is saved to the PreviousAttrValue
property so that it can be restored when removing the modification.
One thing to note is the ApplyModificationsWebConfigXmlDocument
method first calls the SPAspConfigurationFile.EnsureSystemWebServerSection
method to ensure the webServer modules and handlers elements are in the web.config file. This is the reason that even if ApplyWebConfigModifications
is called with no modifications being made the web.config file is still modified.
After the modifications have been applied the web.config file is saved and the original
SPWebConfigFileChanges
is rebuilt to include the new modifications and persisted for future use.
Removing modifications
As alluded to early the Owner
property of the SPWebConfigModification
is used when remove modifications to the web.config.
private static void RemoveMods(SPWebApplication webApp)
{
List<SPWebConfigModification> mods = webApp.WebConfigModifications
.Where(m => m.Owner == "CodeProject")
.ToList();
foreach(SPWebConfigModification mod in mods)
{
webApp.WebConfigModifications.Remove(mod);
}
webApp.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();
}
Here I've used Linq to find all the SPWebConfigModification
in the SPWebApplication
's WebConfigModifications
collection
that have the Owner
equal to "CodeProject". Use the Owner
should help to mitigate the possibility of removing modifications that
may have been done by others. After finding the modifications I remove them from the WebConfigModifications
collection. As described earlier, when
ApplyWebConfigModifications
one of the first steps is to remove current settings. Note, however, this will only apply to EnsureChildNode
and EnsureAttribute
modifications since this is all the RemoveModificationsWebConfigXmlDocument
method handles.
Using Supplemental Config Files
Another way to modify the web.config file for a WebApplication is to make use of a supplemental config file. These xml files have names that begin with webconfig, such as webconfig.codeproject.xml, and are placed in the [SharePoint Root]\Config
folder. When a new WebApplication is created SharePoint will merge the web.config file in that folder with supplemental config files to produce the file web.config file that is placed in the virtual folder for the WebApplication.
Supplemental config files have an actions element with three types of actions, add, update and remove.
<actions>
<add path="configuration/appSettings">
<add key="myAttribute" value="1"/>
</add>
<update path="configuration/appSettings">
<add key="myAttribute" value="42"/>
</update>
<remove path="configuration/mySection" />
<remove path="configuration/appSettings/add[@key = 'myAttribute']" />
</actions>
As you can see from this short example, the path
attribute is a XPath for the element that is being added, updated or removed. In the remove action you can see that
specific attributes can be removed, not just entire elements. More examples of supplemental config files can be found in the [SharePoint Root]\Config
folder
Something to keep in mind when a supplemental config file s that it is applied only when the WebApplication is created, not when a feature is activated. However, supplemental config files have the benefit of being reapplied when an update or service pack is applied, whereas those done using SPWebConfigModification
would need to be manually reapplied.
Conclusion
Applying modifications to the web.config file in SharePoint isn't very complicated, however, since there isn't much documentation on these methods it isn't very clear either. I hope this brief exploration helps to clarify the process.
History
Initial release: 5/23/2012