SPWebConfigModification: configure applicationSettings with a Feature

Features are one of the coolest additions in WSS 3.0 if not the most. Not only they help us as software developers to encapsulate our code in redistributable packages but also simplify mainteinance tasks and deployment to server administrators. Ultimately, we’d like to be able to deploy our site definition with just a walk-through installer that would activate the required features.

Quite often I have to make some changes in the web.config files of the web applications where my solutions are to be deployed. Manifest.xml files of Sharepoint Solutions helps us with this task providing us with some easy ways to alter the web.config files, unfortunately, we are limited at registering safe controls and alter security policies which sometimes is not enough.

What I usually want to do, is to deploy a particular applicationSettings section for an assembly used in my solution, in this example, I have an assembly that uses an httpHandler to retrieve some Xml and I have configured the url of the handler in a settings file, this way, I’ll be able to change it if needed. This is the settings sections I need to include in the web.config file of the web application in order to do that:

<configuration>
  <configSections>
    <sectionGroup name=”applicationSettings” type=”System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″> <section name=”MyAssembly.Properties.Settings” type=”System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″ requirePermission=”false” /></sectionGroup>
  </configSections>
  <applicationSettings>
    <MyAssembly.Properties.Settings>
      <setting name=”HttpHandlerUrl” serializeAs=”String”>
        <value>http://oricode.wordpress.com</value&gt;
      </setting>
    </MyAssembly.Properties.Settings>
  </applicationSettings>
</configuration>

There is no easy way to automate this process. The usual procedure would be to deploy the solution and then ask the administrators to change the web.config files of all the involved web applications manually but this is what I was trying to avoid. I decided to take a look at the new SPWebConfigModification objects in the SPWebApplication object and use them in a Feature Receiver that would modify the web.config file.

At first sight, it seems that this objects are specially suited for this task and that it would be an easy thing to do, but it is not. SPWebConfigModification class is usefull when adding and replacing sections in the configuration files but it’s not easy to modify existing ones by adding new child sections. Basically, what you do is to select a section by providing a xpath and provide the new xml value for the section, but what happens when we don’t want to replace the section but just append new child nodes to it ?

For example in my case, I had to consider the following scenarios:

1. The webconfig file didn’t had the applicationSettings section defined (which is something very usual)
2. The webconfig file already had the applicationSettings section defined (by another assembly) and I had to append the new section for my assembly in it

So I couldn’t just replace the whole section with the xml for my assembly because this might have an impact in any already configured assembly used in the web application that relied in the applicationSettings section.

So here is the code of my FeatureActivated event where I would handle those modifications:

Initialization:

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    SPSite site = (SPSite)properties.Feature.Parent;
    SPWebApplication webApp = site.WebApplication;

Since there is no way to know if the applicationSettings section is already defined in the web.config file, my solution is to execute the modifications as if the section was defined and if that rises an exception, it would mean that it is not and then I could add it. I know it is not an elegant approach and I’d really appreciate it if anybody could come up with a better solution !

try
{
    webApp.WebConfigModifications.Clear();

    SPWebConfigModification configMod = new SPWebConfigModification();
    configMod.Name = @"section[@name='MyAssembly.Properties.Settings']";
    configMod.Path = "configuration/configSections/sectionGroup[@name='applicationSettings']";
    configMod.Sequence = 0;
    configMod.Owner = properties.Feature.DefinitionId.ToString();
    configMod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
    configMod.Value = "";
    webApp.WebConfigModifications.Add(configMod);

    configMod = new SPWebConfigModification();
    configMod.Name = @"MyAssembly.Properties.Settings";
    configMod.Path = "configuration/applicationSettings";
    configMod.Sequence = 0;
    configMod.Owner = properties.Feature.DefinitionId.ToString();
    configMod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
    configMod.Value = "" +
                            "" +
                                "http://oricode.wordpress.com" +
                            "" +
                       "";
    webApp.WebConfigModifications.Add(configMod);

    webApp.Farm.Services.GetValue().WebConfigModifications.Clear();
    webApp.Update();
    webApp.Farm.Services.GetValue().ApplyWebConfigModifications();
}

If the section was already defined, the previous code would launch a <em>SPException</em>. Unfortunately, since the exception is not better typed, there is no way to ensure that the error is caused by the applicationSettings section not being configured (we could always read the exception message that will tell us if so). So if the exception is raised, we have to add the sections as new <em>SPWebConfigModification</em> objects, the previous modifications were added to the <em>WebConfigModifications</em> collection of the <em>SPWebApplication</em> object so there’s no need to add them again.

catch (SPException ex)
{
    SPWebConfigModification configMod = new SPWebConfigModification();
    configMod.Name = @"sectionGroup[@name='applicationSettings']";
    configMod.Path = "configuration/configSections";
    configMod.Sequence = 0;
    configMod.Owner = Guid.NewGuid().ToString();
    configMod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
    configMod.Value = " " +
                      "";
    webApp.WebConfigModifications.Insert(0, configMod);

    configMod = new SPWebConfigModification();
    configMod.Name = @"applicationSettings";
    configMod.Path = "configuration";
    configMod.Sequence = 0;
    configMod.Owner = Guid.NewGuid().ToString();
    configMod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
    configMod.Value = "";
    webApp.WebConfigModifications.Insert(0, configMod);

    webApp.Update();
    webApp.Farm.Services.GetValue().ApplyWebConfigModifications();
}

Note that this two last configuration sections have their Owner property set to a random Guid string. This is because the owner property is used in the feature deactivating event for removing the web.config changes, however, I can’t remove the applicationSettings sections because after my feature is activated, some other application settings might be registered in those sections by the server administrator and If then my feature was deactivated, all of them would be deactivated as well.

There are also a lot of issues I’ve faced when using SPWebConfigModification objects, like modifications that are persisted in the WebConfigModifications collections of the webApplication object before any call to the update() method is made (this is why I’ve set the Clear() methods of the collection at the start of the code) and other similar issues. Feel free to comment any strange behaviour you might find and I’ll try to help you If possible.

And that’s it, activating this feature will modify the web.config file of the web application as desired. However, I hope in future releases of Sharepoint object model we will see a better way to achieve this.

To end, I’d like to point that this solution should be tested in a farm deployment where multiple servers are involved, use with caution in that scenarios !

That’s all for now :-) as always, comments are welcome !

About these ads

3 Responses to SPWebConfigModification: configure applicationSettings with a Feature

  1. Thomas says:

    Hi, thanks for the research! I’m having an issue:

    When I run my configuration program that collects value from the user and then configures web.config using the SPWebConfigModification approach, it seems that I must run the program TWICE, but ONLY the first time.

    After the first set of SPWebConfigModifications have been applied, my web.config is unchanged. After the second run, the web.config is updated, and hereafter, the web.config is updated immediately, as expected.

    Any ideas?

  2. oricode says:

    Yep, this is some of that strange behaviour I was talking about. Please, let me know if you get more info on that !

  3. Thomas says:

    Oh, I think I found it: I called ApplyWebConfigModifications BEFORE webApp.Update() – now my code looks like this:

    foreach (ModificationEntry entry in Entries)
    {
    webApp.WebConfigModifications.Add(
    CreateModification(entry)
    );
    }
    webApp.Update();
    webApp.Farm.Services.GetValue().ApplyWebConfigModifications();

    …and that seems to work.

    Thanks anyway!
    Thomas

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: