Sunday, 11 May 2008

Introducing the SharePoint Config Store for developers

Today I want to introduce something I've been working on recently which could be of use to you if you're a SharePoint developer. Often when developing SharePoint solutions which require coding, the developer faces a decision about what to do with values he/she doesn't want to 'hardcode' into the C# or VB.Net code. Example values for a SharePoint site/application/control could be:

'AdministratorEmail' - 'bob@somewhere.com'
'SendWorkflowEmails' - 'true'

Generally we avoid hardcoding such values, since if the value needs to be changed we have to recompile the code, test and redeploy. So, alternatives to hardcoding which folks might consider could be:

  • store values in appSettings section of web.config
  • store values in different places, e.g. Feature properties, event handler registration data, etc.
  • store values in a custom SQL table
  • store values in a SharePoint list

Personally, although I like the facility to store complex custom config sections in web.config, I'm not a big fan of appSettings. If I need to change a value, I have to connect to the server using Remote Desktop and open and modify the file - if I'm in a server farm with multiple front-ends, I need to repeat this for each, and I'm also causing the next request to be slow because the app domain will unload to refresh the config. Going through the other options, the second isn't great because we'd need to be reactivating Features/event receiver registrations every time (even more hassle), though the third (using SQL) is probably fine, unless I want a front-end to make modifying the values easier, in which case we'd have some work to do.

So storing config values in a SharePoint list could be nice, and is the basis for my solution. Now I'd bet lots of people are doing this already - it's pretty quick to write some simple code to fetch the value, and this means we avoid the hardcoding problem. However, the Config Store 'framework' goes further than this - for one thing it's highly-optimized so you can be sure there is no negative performance impact, but there are also some bonuses in terms of where it can used from and the ease of deployment. So what I hope to show is that the Config Store takes things quite a bit further than just a simple implementation of 'retrieving config values from a list'.

Details (reproduced from the Codeplex site)

The list used to store config items looks like this (N.B. the items shown are my examples, you'd add your own):

ConfigStoreList.jpg
There is a special content type associated with the list, so adding a new item is easy:

ConfigItemContentType.jpg

..and the content type is defined so that all the required fields are mandatory:

AddNewConfigItem.jpg

Retrieving values


Once a value has been added to the Config Store, it can be retrieved in code as follows (you'll also need to add a reference to the Config Store assembly and 'using' statement of course):

string sAdminEmail = ConfigStore.GetValue("MyApplication", "AdminEmail");


Note that there is also a method to retrieve multiple values with a single query. This avoids the need to perform multiple queries, so should be used for performance reasons if your control/page will retrieve many items from the Config Store - think of it as a best practise. The code is slightly more involved, but should make sense when you think it through. We create a generic List of 'ConfigIdentifiers' (a ConfigIdentifier specifies the category and name of the item e.g. 'MyApplication', 'AdminEmail') and pass it to the 'GetMultipleItems()' method:


List<ConfigIdentifier> configIds = new List<ConfigIdentifier>();

ConfigIdentifier adminEmail = new ConfigIdentifier("MyApplication", "AdminEmail");
ConfigIdentifier sendMails = new ConfigIdentifier("MyApplication", "SendWorkflowEmails");
configIds.Add(adminEmail);
configIds.Add(sendMails);
Dictionary<ConfigIdentifier, string> configItems = ConfigStore.GetMultipleValues(configIds);
string sAdminEmail = configItems[adminEmail];
string sSendMails = configItems[sendMails];

..the method returns a generic Dictionary containing the values, and we retrieve each one by passing the respective ConfigIdentifier we created earlier to the indexer.


Other notes
  • All items are wrapped up in a Solution/Feature so there is no need to manually create site columns/content types/the Config Store list etc. There is also an install script for you to easily install the Solution.

  • Config items are cached in memory, so where possible there won't even be a database lookup!

  • The Config Store is also designed to operate where no SPContext is present e.g. a list event receiver. In this scenario, it will look for values in your SharePoint web application's web.config file to establish the URL for the site containing the Config Store (N.B. these web.config keys get automatically added when the Config Store is installed to your site). This also means it can be used outside your SharePoint application, e.g. a console app.

  • The Config Store can be moved from it's default location of the root web for your site. For example sites I create usually have a hidden 'config' web, so I put the Config Store in here, along with other items. (To do this, create a new list (in whatever child web you want) from the 'Configuration Store list' template (added during the install), and modify the 'ConfigWebName'/'ConfigListName' keys which were added to your web.config to point to the new location. As an alternative if you already added 100 items which you don't want to recreate, you could use my other tool, the SharePoint Content Deployment Wizard at http://www.codeplex.com/SPDeploymentWizard to move the list.)

  • All source code and Solution/Feature files are included, so if you want to change anything, you can

  • Installation instructions are in the readme.txt in the download


Summary

Hopefully you might agree that the Config Store goes beyond a simple implementation of 'storing config values in a list'. For me, the key aspects are the caching and the fact that the entire solution is 'joined up', so it's easy to deploy quickly and reliably as a piece of functionality. Many organizations are probably at the stage where their SharePoint codebase is becoming mature and perhaps based on a foundation of a 'core API' - the Config Store could provide a useful piece in such a toolbox.


You can download the Config Store framework and all the source code from www.codeplex.com/SPConfigStore.

13 comments:

Richard Edwards said...

This looks great Chris. Thanks for the contribution!

Unknown said...

Hi Chris,

What if Sharepoint is installed as a server farm. Since the configuration is stored in memory the list values have to be entered on each server.In this case will SPPersistedObject be a better choice.

Regards,
Jagannath

Chris O'Brien said...

Hi Jagannath,

Interesting thought, and something which has been suggested to me in the past. However, in my view one of the main points of the Config Store is that it IS a SharePoint list! Trying not to think about it as a developer for a moment, this has important advantages. Wherever I've implemented it, the amount of memory across the servers used to cache the list items hasn't been an issue.

Valid discussion though.

Thanks,

Chris.

Anonymous said...

Hi!

Great idea, I had the same concerns about using the web.config. I think that your solution is brilliant as it can be used by many sites/applications. I have a suggestion for additional functionality. Having the ability to "schedule" the setting changes would be fantastic. For example, beeing able to apply a configuration change so it becomes effective at midnight on a certain day would be very helpfull in certain cases.

Great work!

Chris O'Brien said...

Nic,

Interesting idea :-)

Chris.

Anonymous said...

Hi Chris,
Nice work. Sure many people will find the config store useful. Here a couple things I had difficulty with.

There are a couple typos in the readme. Several of the appsetting keys aren't quite right.

With'ApplyWebConfigModifications' Feature property set to 'True', I had to change a couple lines in the featureReceiver so that entries would remove themselves:


configMod.Name = string.Format("add[@name='{0}']", sModificationName);

Changed to...

configMod.Name = string.Format("add[@key='{0}'][@value='{1}']", sKey, sValue);

And
configMod.Name = string.Format("add[@name='{0}']", sModificationName);

Changed to...

configMod.Name = string.Format("add[@expressionPrefix='{0}'][@type='{1}']", sPrefix, sType);

Excuse me if I'm wrong - I'm new to this. But it appears to have fixed my problem.

Chris O'Brien said...

Hi George,

Many thanks for this, it's much appreciated. However, I was pretty sure I fixed the web.config key removal in the most recent version uploaded to Codeplex. Did you find the issue in the 2.0.0.0 version or 1.0.0.0?

Cheers,

Chris.

DavidBarrows said...

Hi Chris, I echo the sentiments, great work.

But, (and I'm not sure if this is the exact issue George was reporting, but I've not tried making his modification); I also wanted to report, I seem to be experiencing an issue where, even though the ApplyWebConfigModifications flag is set to True in feature.xml, when the feature is deactivated, the FeatureDeactivating code is not removing your entries from the web.config file. Thus, upon reactivating the feature (e.g. during development when the feature is repeatedly activated and deactivated), the entries keep getting written to the web.config file over and over again. Just FYI. In any case, very useful tool and thanks for making it. -Dave Barrows

Chris O'Brien said...

Hi Dave,

Thanks for the positive words. In terms of the issue, I've definitely seen it myself but I did make some changes in this area for version 2.0.0.0 and was sure they tested out OK.

Do you know which version you downloaded from Codeplex/if it was 2.0.0.0?

Thanks,

Chris.

DavidBarrows said...

Hi Chris,

I just followed the link on your blog (above); on CodePlex it says:

Update: Release 2.1.0.0 uploaded 20 April 2009 with important fix - see Release 2.1.0.0 page for release notes

Chris O'Brien said...

Dave,

Thanks for coming back to me - I'll retest this week and post here.

Cheers,

Chris.

Chris O'Brien said...

@Dave,

You were right unfortunately - think this was a regression bug in the last release. Now fixed in release 2.1.0.1 at http://spconfigstore.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=27194.

Sorry about that! Appreciate you pointing it out :-)

Cheers,

Chris.

Anonymous said...

Awesome work, your head is in the right place, bringing sharepoint one step closer to a viable alternative for a web application

Nice Job!!!